【gitをソフト開発で使いこなそう!:番外編】デジタル署名のよくある誤解

連載記事一覧

terapotan.hatenablog.jp

あなたは、「デジタル署名とはこういうものだ!」と思っていませんか?

  1. デジタル署名は公開鍵暗号から作れる。
  2. 秘密鍵で暗号化して、公開鍵で復号する
  3. 復号したものが、送られてきた平文と一致すれば平文の送信者が送ったことを証明出来る

……実はこれ、デジタル署名のよくある間違った説明です。

今回の記事では、「なぜこの説明が間違っているのか」「正しいデジタル署名の説明とは何なのか」について解説していきます。

正しいデジタル署名の説明

先ほどの説明がなぜ間違っているかを説明する前に、まず正しいデジタル署名の説明を行います。

デジタル署名で守れるもの

ここに、Aさんとチケットを販売するお店があったとします。

図

Aさんは、上の図のような手順でチケットを購入することが出来ます。(チケットの購入は、インターネット上で行います。)

ですが、このままでは誰かが、Aさんが「来月のコンサートのチケットを2枚購入する」というメッセージを、「来月のコンサートのチケットを200枚購入する」というメッセージに書き換えてしまう(改ざん)かもしれませんし、Aさんになりすまして「来月のコンサートのチケットを追加で100枚購入する」というメッセージを送ってしまうかもしれません(なりすまし)。

なりすまし

改ざん

また、チケット販売店がAさんからチケット代金を受け取るときに、Aさんが「え?私そんなメッセージ送ってませんよ? きっと誰かがなりすまして送ったんですよ!」と言われたときに、そうでないことを証明する手段もありません。(否認)

脅威を図で説明

デジタル署名を使うとこれらの脅威から守ることが出来ます。

Column:あれ?盗聴からは守れないの?

デジタル署名で守れる脅威の中に盗聴が入ってないのが分かると思います。残念ながら、盗聴はデジタル署名だけでは防ぐことが出来ません。盗聴も防ぎたいのであれば、通信内容を暗号化しておく必要があります。

デジタル署名とは?

先ほど紹介した例を使って、デジタル署名の仕組みについて解説します。

まずAさんは、署名鍵検証鍵と呼ばれる二種類の鍵を作ります。
署名鍵は外に漏れ出ないように自分以外には秘密にしておきます。
逆に検証鍵は、公開しておきます。

Aさんは、送りたいメッセージと署名鍵(厳密に言うと検証鍵も)を使って署名を作成し、メッセージと署名をチケット販売店に送ります。

検証鍵と署名鍵

チケット販売店は、Aさんから送られてきたメッセージ、署名と公開されている検証鍵を使って、署名が正しいかどうか検証します。

ここで図を挿入

検証では、「送られてきた署名が、送られてきたメッセージとAさんが作成した秘密鍵を使って作られたもの」であることを確かめます。
そうであることが確かめられれば、「このメッセージは間違いなくAさんが送信したものだ」ということを確認できます。(Aさんが作成した秘密鍵を持っているのは、Aさん以外にいないはずだから。)

これで否認となりすましが出来なくなります。

また、検証は送られてきたメッセージと署名を使って行われます。そのため、誰かがメッセージだけ変えて改ざんをしようとしても、そのメッセージに合った署名を作ることが出来ないため、改ざんすることは出来ません。

Column:その検証鍵、本当にAさんのもの?

上のデジタル署名でもし、Aさんの検証鍵が誰かの検証鍵にすり替えられていたら、どうでしょう。すり替えが行われると、Aさんの署名鍵を持っていなくても検証は正しく行われてしまいます。よって、その検証鍵が確かにAさんが作ったものであることを証明する仕組みが必要です。この仕組みをPKI(公開鍵基盤)と言います。

なぜあの説明は間違っているのか?

さて、本題に戻ります。
最初に言った通り、よくデジタル署名の仕組みとして、

  1. デジタル署名は公開鍵暗号から作れる。
  2. (公開鍵暗号を使って)秘密鍵で暗号化して、公開鍵で復号する
  3. 復号したものが、送られてきた平文と一致すれば平文の送信者が送ったことを証明出来る

よくある間違った説明

のように説明されます。しかし、これは間違った説明です。

なぜ間違った説明と言えるのでしょうか。理由は二つあります。

  1. デジタル署名そのものの、説明になっていない
  2. 公開鍵暗号において、秘密鍵で暗号化して公開鍵で復号することは出来ない

理由1:デジタル署名そのものの、説明になっていない

もし上の説明のようにデジタル署名を作れるとしても、そのような仕組みを使えば、デジタル署名の仕組みを作れるというだけであって、デジタル署名そのものを説明したことになっていないのです。

上の文を捕捉する図(仮に出来たとしても、公開鍵暗号はデジタル署名の実現方法の一つに過ぎないことを説明すること)

理由2:秘密鍵で暗号化して公開鍵で復号することは出来ない

そして公開鍵で暗号化、秘密鍵で復号が出来るからといって、「(公開鍵暗号を使って)秘密鍵で暗号化して、公開鍵で復号する」ことは出来ません。

……なぜそう言い切れるのでしょうか。同じ鍵ですから、なんだか入れ替えられそうな気がします。
これを考えるには、まず公開鍵暗号とはどのようなものだったかを改めて考える必要があります。

公開鍵暗号というのは、

  1. 秘密鍵と公開鍵を生成する。
  2. 平文と公開鍵を使って暗号文を生成する
  3. 暗号文と秘密鍵を使って平文に復号する。

というものであることが分かります。
公開鍵暗号には、様々な種類(RSA暗号ElGamal暗号など)がありますが、(実際にやっている処理は違うにしろ)上の1~3の入力と出力は、どの公開鍵暗号であっても共通ですし、どんな公開鍵暗号であっても上のように使えば、動作することが保証されています。(というより、上のように使うことを想定して全ての公開鍵暗号は、作られている。)

では、先ほど誤りと言った説明で上と同じように暗号化・復号の手順を書いてみます。

  1. 秘密鍵と公開鍵を生成する。
  2. 平文と秘密鍵を使って暗号文を生成する
  3. 暗号文と公開鍵を使って平文に復号する

最初に挙げた手順と見比べると、本来公開鍵を入力すべきところに秘密鍵が、公開鍵を入力すべきところに秘密鍵が入力されています。
秘密鍵と公開鍵は、同じ「鍵」という文字がついてはいますが、別物です。(全ての公開鍵暗号は、最初に挙げた手順で行うことを想定して作っている。全て公開鍵暗号で、鍵をひっくり返して動くことはない。)

ですから、秘密鍵から公開鍵、公開鍵から秘密鍵に入力する鍵を変えることは出来ません。

下の4つのコラムは、もっと詳しく知りたい人向けに書いています。今の段階で理解できなければ、無理に理解する必要はありません。(というか、かえって混乱するかもしれない。)(4つ目のコラムは、コラムの中で数式が書けないため、本文に書いています。)

Column:こうだったらいけるんじゃね?

ここまでの説明を聞いて「いや、秘密鍵で復号、公開鍵で暗号化だったら公開鍵暗号を使ってデジタル署名が作れる!」「RSA暗号だったら、秘密鍵と公開鍵ひっくり返せるのでは?」などと思う方がいらっしゃるかもしれません。(一応下のコラムで説明します。)
しかし、よく考えてみてください。仮にそれらが全て正しかったとしても理由1で述べた通り、「そうやればデジタル署名が作れる」ことを証明したに過ぎないのです。

結局どこまでいっても「デジタル署名そのもの」の説明になることはありません。

Column:秘密鍵で復号、公開鍵で暗号化

上の説明では、秘密鍵で暗号化、公開鍵で復号は出来ないと説明しました。ですが、秘密鍵で復号、公開鍵で暗号化であれば理屈上は可能です。(入力する鍵を変えていないため。)
秘密鍵で平文を復号???と思うかもしれませんが、これは単に秘密鍵と平文を復号するアルゴリズムに入力することを指しています。

ですが、これを行うためには「同じ鍵・同じ平文で暗号化すれば、毎回必ず同じ暗号文が出力される」公開鍵暗号を使う必要があります。
「えっ?何を当たり前のことを。そんな性質のどの公開鍵暗号も持ってるんじゃないの?」と思われるかもしれませんが、全ての公開鍵暗号がこの性質を持っているとは限らないのです。(性質を持っていない公開鍵暗号の例:ElGamal暗号RSA-OAEPなど)

先ほど説明した性質を持っていない、ということは仮に正しい秘密鍵で復号されたものを公開鍵で暗号化して検証しようとしても、正しく検証出来ない可能性があるということになります。
まぐれで検証が通ったり、通らなかったりするデジタル署名はもはやデジタル署名ではありません。

Column:RSA暗号RSA署名

先ほどのコラムで「全ての公開鍵暗号で出来るわけではない」と書きました。ということは、出来る公開鍵暗号もあるということです。
「同じ鍵・同じ平文で暗号化すれば、毎回必ず同じ暗号文が出力される」という性質を持つ公開鍵暗号として一番有名なのはRSA暗号でしょう。
RSA暗号からであれば、「秘密鍵で復号、公開鍵で暗号化」を行うことでデジタル署名を作ることが出来ます。(理論上は。実際のソフトウェアで「秘密鍵で復号、公開鍵で暗号化」をやってもエラーが出て動かないだろう。少なくともOpenSSL上では動作しなかった。)
実際この考え方を使っているデジタル署名が存在します。それがRSA署名です。

ですが、先ほどのコラムでも言った通りこれが出来るのは一部の公開鍵暗号に限られます。これだけを見て「全ての公開鍵暗号でデジタル署名が作れる」とは言えないでしょう。

Column:RSA暗号なら「秘密鍵で暗号化、公開鍵で復号」出来る!?

本文では全ての公開鍵暗号で鍵をひっくり返して動くことはない、(秘密鍵で暗号化、公開鍵で復号を行うことは出来ない。)と説明しました。
ですが、他の公開鍵暗号で出来なくてもRSA暗号であれば秘密鍵で暗号化、公開鍵で復号することが出来るという主張を耳にします。

……本当なのでしょうか。それを確かめるために、RSA暗号がどのような仕組みで動いているのか見ていくことにします。

平文をm,公開鍵をeとします。(正確にはNも公開鍵です。)
このとき、次の式を計算することで暗号文cを求めることが出来ます。

{ \displaystyle
c=m^ e \text{ mod }N
}

また、秘密鍵dとします。
このとき次の式を計算することで、暗号文cを平文mに復号することが出来ます。

{ \displaystyle
m=c^ d \text{ mod }N
}

なんだか難しそうな数式が出てきました。ですが、今回はこの数式の意味を理解する必要はありません。eが公開鍵でd秘密鍵ということが何となく分かれば大丈夫です。

m^ ec^ dというのはそれぞれme乗、cd乗という意味です。
これだけを見ると、秘密鍵であるdと公開鍵であるeを逆にしても出来そうです。

{ \displaystyle
c=m^ d \text{ mod }N
}

{ \displaystyle
m=c^ e \text{ mod }N
}

このようにRSA暗号式だけを見ると秘密鍵で暗号化、公開鍵で復号出来そうに思えます。

しかしながら、実際にRSA暗号の処理を行うソフトで秘密鍵で暗号化、公開鍵で復号を行うことは出来ません
式を見て出来そうなのに、ソフトでは動かないとはどういうことなのでしょうか。というより、本当に動かないのでしょうか。

「OpenSSL」という、公開鍵暗号共通鍵暗号などの処理を行うことが出来るソフトを使って、実際にやってみましょう。

Gitの連載から来られた方は、GitBash上で下のコマンドを実行することが出来ます。
そうではなく、使用しているOSがWindowsの場合は、追加でOpenSSLをインストールする必要があります。

インストール方法については、下の記事などを参考にしてください。

www.atmarkit.co.jp

まず初めに、秘密鍵を作成します。

openssl genrsa 2048 > private_key.pem

このコマンドを実行すると、private_key.pemというファイルが作成されます。名前の通りこのファイルが秘密鍵となります。

続いて公開鍵を作成します。

openssl rsa -pubout < private_key.pem > public_key.pem

秘密鍵の作成と同じく、public_key.pemというファイルが作成されます。
このファイルが公開鍵となります。

暗号化するファイルを作成しておきます。ファイルの名前の通りこれが平文となります。

echo 'Hello!' > plain.txt

作成されたplain.txtを開くと「Hello!」と書かれているはずです。

さて、ここからが本番です。

下のコマンドでは、秘密鍵を使って平文を暗号化しています。暗号化した結果はplain.encryptedというファイルに書き込まれるようになっています。上手くいくでしょうか。

openssl rsautl -encrypt -inkey private_key.pem < plain.txt > plain.encrypted

エラーメッセージが表示されずにコマンドの実行が終了したでしょうか。
また、plain.encryptedというファイルが作成されているでしょう。

cat plain.encrypted

というコマンドを実行すると、plain.encryptedの中身を見ることが出来ます。文字化けしていてよく分かりませんが、「秘密鍵で暗号化」は上手くいったように見えます。

最後に「公開鍵で復号」が出来るかどうか確かめてみます。
復号した結果はplain.decryptedに書き込まれます。復号した結果がplain.txt、つまり平文と一致すれば「公開鍵で復号」出来たことになります。

openssl rsautl -decrypt -pubin -inkey public_key.pem < plain.encrypted > plain.decrypted

上のコマンドを実行してみると、次のようなエラーメッセージが出てしまいました。

A private key is needed for this operation

「この操作には、秘密鍵が必要です」と書かれています。

以上の結果から、(少なくともOpenSSLでは)RSA暗号であっても「秘密鍵で暗号化、公開鍵で復号」出来ないことが分かりました。

Column:本当に秘密鍵で暗号化が上手くいったのか?

実は、OpenSSLの場合秘密鍵のファイルの中に公開鍵の情報が含まれているのです。恐らく、秘密鍵の情報で暗号化を行ったのではなく実際には、公開鍵の情報を使って暗号化したのではないでしょうか。

その証拠に、公開鍵で復号の手順で復号に秘密鍵を使う(-pubinオプションは外す必要がある)と平文に復号されます。(もし秘密鍵で暗号化されたのなら、秘密鍵で暗号化して秘密鍵で復号されたことになってしまう。)
OpenSSLの実装を実際に見たわけではないので推測にすぎませんが。

どちらにせよ、これはOpenSSLだからたまたま動いただけで他のソフトで動く保証はどこにもありません。RSA暗号であっても「秘密鍵で暗号化、公開鍵で復号」は出来ないと言い切った方が無難です。

う-ん、よく分からん!

この記事を読んで、疑問に思うことがあったときは、気軽にコメント欄や私のTwitterから質問してください。(すぐに答えを返せるとは限りませんが。)

参考文献

暗号技術のすべて

この書籍のp.502~p.505で、「よくあるデジタル署名の誤解」について解説されています。

共通鍵暗号公開鍵暗号・メッセージ認証コード・ハッシュ関数など(シーザー暗号などの古典暗号も解説されている!)まさに「暗号技術のすべて」が詳しく解説されています。

普通、暗号技術がどのような仕組みで動いているのかを、詳しく追おうとすると、どうしても数学の知識が必要になってしまいます。ですが、この書籍では仕組みの理解に必要な数学知識の説明を一緒にやってくれます。

「暗号技術をきっちりと押さえておきたい!」という人にはぴったりの本でしょう。(ただ、約700ページとかなり分量が多いです。最初のうちは興味のあるところから読んでいったほうがいいでしょう。私もまだ読み切れてない……)

暗号技術のすべて

暗号技術のすべて

  • 作者:IPUSIRON
  • 発売日: 2017/08/03
  • メディア: 単行本(ソフトカバー)

電子署名=『秘密鍵で暗号化』」という良くある誤解の話 など

なぜ「秘密鍵で暗号化」が間違っているのか解説している記事もいくつかあります。
一度目を通しておくといいでしょう。

qiita.com

qiita.com

連載記事一覧

terapotan.hatenablog.jp