opensslとかディジタル署名とか

情報処理技術者試験でよく出てくる、メッセージのやりとりに用いるディジタル署名
超頻出問題なのだが、これを実際に使っている部分を目で見たり、手でいじってみたりするのはなかなか敷居が高い。しかしopensslなどフリーの環境だけで試せるのだから、やってみないのはもったいない。

というわけでここでは、実際に手元のパソコンで動かしてみるとどうなるか、やり方をまとめてみた。環境はMac OS X 10.5.8。ターミナル.appからopensslを使います。Linuxならばopensslパッケージを入れてください(もう最近のは最初から入ってるけど)。Windowsならばcygwinでどうぞ。

公開鍵と秘密鍵の作成

ではまずは教科書通り、秘密鍵と公開鍵の鍵ペアを作る。

秘密鍵の作成
$ openssl genrsa -out private.pem 1024
  • genrsaでRSA Private Keyを作成する。
  • 最後の数字は何も入れないと512bitがデフォルト。ここでは1024bitで作ってみる。

なお拡張子はkeyとして"private.key"とかする人もいる。ただのファイル名なので、この辺はどうでもいい。.pemなのは、PEM形式だということを言いたいから。

公開鍵の作成

いま作った秘密鍵から、公開鍵を作成する。-puboutオプションを付ければよい。

$ openssl rsa -in private.pem -pubout -out public.pem

これで、以下の鍵ペアができた。

  • 秘密鍵:private.pem
  • 公開鍵:public.pem

メッセージへのディジタル署名の作成

では、鍵ペアが用意できたので実際にディジタル署名を付けたメッセージを手で作って送ってみよう。ケースとしてこんなのを考える。

AliceがBobに以下のようなメッセージを送る。

こんにちは。
明日は10:00に時計台で待ち合わせをしましょう。
Aliceより

まず、Aliceは送信文のハッシュ値を作る。sha-1(160bit)で作るのが普通ですかね。メッセージが、message.txtというファイルに書かれているとする。

$ sha1sum message.txt
325b367c3a9b07ad7951e8d015d63f03136b9deb  message.txt

このハッシュから、Aliceは自分の秘密鍵を使ってディジタル署名を作る。

$ echo "325b367c3a9b07ad7951e8d015d63f03136b9deb" | openssl rsautl -sign -inkey private.pem > alice.sig

これで出来上がったディジタル署名、alice.sigはバイナリファイル。

で、今はメッセージは電子メールで送るのが普通(だよね?)。例えばS/MIMEの場合、ディジタル署名Base64エンコーディングして、メール本文と共にマルチパートで送ることになる。でもまぁ、ここでは手動で目で見ながらやってみるのが目的なので、このまま行きます。

メッセージ受信とディジタル署名の確認

さて、Bobはこの署名ファイルが付いた、こういうメッセージを受け取った。

こんにちは。
明日は10:00に時計台で待ち合わせをしましょう。
Aliceより

※[alice.sig] が添付

では、この内容が改ざんされていないかを、受け取ったBobが調べてみよう。

まずBobは、メッセージ本文をmessage.txtとして保存し、sha1sumを自分で作ってみる。

$ sha1sum message.txt
325b367c3a9b07ad7951e8d015d63f03136b9deb  message.txt

先ほど我々が計算した通り、"325b367c3a9b07ad7951e8d015d63f03136b9deb"が出てきた。
一方、Aliceが公開している公開鍵ファイル、public.pemをどこかで取得して(きっとAliceは自分のWebページを持っていて、そこで配っているのでしょう)、その公開鍵を使って電子署名を復号してみる。

$ openssl rsautl -verify -in alice.sig -pubin -inkey public.pem
325b367c3a9b07ad7951e8d015d63f03136b9deb

この値は、自分で計算したメッセージのハッシュ値と一致した。

ということは、このメッセージは「Aliceの鍵を用いて」「改ざんされずに」送られたことが分かる。めでたしめでたし。明日は遅れないようにしないとね。

改ざんされた場合

では、AliceとBobの仲に嫉妬したMarvinが、Aliceの送信メールを何らかの手段でぶっこ抜き、代わりにこんなメールをBobに送ったとする。

こんにちは。
あなたのことなんて大嫌い。もうお別れね。
Aliceより

※[alice.sig] が添付

(Marvinは、メール本文のみ書き換えて署名はそのままにしたとする。)

このメールを受け取ったBobは、おろおろしながらメッセージ部分のハッシュを取ると、

$ sha1sum message.txt
30762f44b44b6d9ce47b3925656ec998aea791c9  message.txt

"30762f44b44b6d9ce47b3925656ec998aea791c9"だった。(面倒だけど、この例では本文をいったんmessage.txtとして保存してそのファイルのハッシュを取っている)

一方、添付されたalice.sig(ディジタル署名)ファイルをAliceの公開鍵で復号すると、それは……。

$ openssl rsautl -verify -in alice.sig -pubin -inkey public.pem
325b367c3a9b07ad7951e8d015d63f03136b9deb

"325b367c3a9b07ad7951e8d015d63f03136b9deb" 。値が違う!

ということは、このメッセージはAliceが送ったものから、何かしらの改ざんを受けたモノである。と判断できることになる。二人の仲は無事でした。

署名を偽造する

では、署名をなんとか偽造しようとしたらどうだろう。Aliceの秘密鍵は手に入らないので、Marvinは自分の秘密鍵でハッシュから署名したものを付けて、それっぽく送ってみたとする。

こんにちは。
あなたのことなんて大嫌い。もうお別れね。
Aliceより

※[alice.sig] が添付(実はMarvinが自分の秘密鍵で作ったもの)

この際には、受信したBobは、「Aliceの公開鍵を使い、Marvinの秘密鍵で暗号化されたものを復号する」ということになる。すると以下の通りエラーとなり復号できないため、やっぱり正しいメッセージではないことが分かる。

$ openssl rsautl -verify -in alice.sig -pubin -inkey public.pem
RSA operation error
140735086923036:error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01:rsa_pk1.c:100:
140735086923036:error:04067072:rsa routines:RSA_EAY_PUBLIC_DECRYPT:padding check failed:rsa_eay.c:721:

このように、ディジタル署名の一番シンプルな例では、「平文+署名」で改ざん検知ができる。暗号化ももちろんできるのだが、ここでは分かりやすくするためしていない。

また、上記の例では書いていないけど、この逆の機能と言うべき否認の防止もできる。あるメッセージとそのディジタル署名に対して、Aliceの公開鍵で正しく正当性が確認できるということは、逆に言うとそのディジタル署名はAliceの秘密鍵で作られたものだということ。

ということは、あるメッセージが署名付きで出されたら、それを後でAliceが「いや、それは私の発言じゃないよ」と言うことはできない。なぜなら、その署名はAliceの秘密鍵でしか作れない、イコール、Aliceが書いたものだから……。これが「否認防止」。

認証局と証明書

この辺で、意図的に書いていなかった重要な問題がある。

今まで、BobはAliceの公開鍵を正しく入手したという前提でサラリと書いてきたけれど、コトはそんなに単純じゃない。Aliceが直接USBメモリを持ってBobの部屋にやってきて、「これ、あたしの鍵だから。よろしくね」と渡してくれればいいのだけど、現実的にはそんなことはないですね。部屋に女の子来ないし(そういう話じゃない)。

普通は、公開鍵はメールで送られるなりWebで公開するなりして送られてくる。でも、それを送ってくれた人が本当にAliceかどうかは分からない……というのがキモ。実はMarvinが、「あたしAlice、よろしくね」とAliceのフリをして自分の公開鍵をBobにメールしているのかもしれない。

ということで、証明書が必要になってくる。信頼できる第三者(CA: 認証局)が、「これは確かにAliceの鍵だね」と言ってくれれば、Bobは安心してAliceの公開鍵をゲットできて解決する。

ではさっそく、試しに認証局の署名付きの証明書を作ってみよう……と思うとカネがかかっちまう。ということで、opensslでとりあえず試してみたければ、オレオレ証明書が以下のコマンドで作れる。

オレオレ証明書作成
$ openssl req -new -x509 -out oreore.crt -key private.pem -days 365
pkcs12の作成
$ openssl pkcs12 -export -in oreore.crt -inkey private.pem -out bundle.p12

pkcs12とは、秘密鍵と証明書ファイルをパッケージ化したファイル形式。これでWindowsなどではダブルクリックで証明書ストアにインポートできる。

過去問の例

ということで、一度手で動かしてみれば、よく分かるようになると思う。

応用情報技術者 平成22年春期 午前問40

ディジタル署名を生成するときに,発信者がメッセージのハッシュ値ディジタル署名に変換するのに使う鍵はどれか。
解答:発信者の秘密鍵

基本情報技術者 平成22年春期 午前問40

ディジタル署名付きのメッセージをメールで受信した。受信したメッセージのディジタル署名を検証することによって,確認できることはどれか。
解答:メッセージが、改ざんされていないこと