読者です 読者をやめる 読者になる 読者になる

httpsとかSSLとかについての理解メモ

まえがき

今日はKMCアドベントカレンダーでcosさんがhttpsについて書いてくれるということなので、割とタイムリーかもしれない。実はとあるやってみようとしたことが二つあったのだが両方ともこのあたり知識が関係してきたので、さすがにちょっと理解しないといけないなと思って勉強してみた。

まず公開鍵暗号について

最近のSSHアドベントカレンダーで語られたり語られてなかったりする気もするが、簡単に。
公開鍵暗号に登場する鍵は二つ。「かける鍵」「解く鍵」だ。日常生活の中ではこの二つはイコールで結ばれてるが、ここではこの二つの鍵は異なるものであると考えてほしい。すなわち、「かける鍵」でかけた鍵は「かける鍵」で解くことはできない。「解く鍵」についても同様であるということだ。
公開鍵暗号とは、この「かける鍵」を広くユーザーに配布し、「解く鍵」を自分だけが持っておくことで、安全な通信を実現する方式だ。公開鍵とはこの広く配布される「かける鍵」のことを指している。

SSL通信の大まかな流れ

1 クライアントのブラウザが「https://hogehoge~」なサイトにアクセスする。

2 サーバー側が証明書をブラウザに送る。この証明書の構成要素は前述の公開鍵とか、このサイトはちゃんとしたサイトだよっていうしるしだったりそんな感じ。*1

3 ブラウザはこの中の公開鍵を使って通信を暗号化し、サーバーは持っている「解く鍵」(これを「秘密鍵」と呼ぶ)を使って暗号を解いて通信を解釈する。

たぶんこんな感じ。

オレオレ証明書、自己証明書とは?

さて、単に通信の安全性だけを考えるなら、別に証明書なんか必要なくて、鍵をそのまま配ってしまえばいいという話だ。そうしなくて証明書なんてものをわざわざ第三機関に着けてもらってるのは、通信の暗号うんぬんは関係なくて、「この通信の先はちゃんとした(清廉潔白の、本物の)機関ですよ」という証明を付随させるためである。
さてこの電子証明書、偽造は技術的に不可能なのだが、普通に作ることはできる。つまり誰でも発行しようと思えば電子証明書は発行できる。この「信頼できる第三者機関」というのはあらかじめブラウザに登録されているのだが、そうでない機関が発行した証明書を感知するとブラウザは「この証明書、なんだか怪しいですよ」と教えてくれる。これが自己証明書、オレオレ証明書の正体だ。

mechanizeのOpenSSL::SSL::VERIFY_NONEとはなんだったのか

そもそも発端は後輩のrubyのmechanizeというwebアクセスエミュレートライブラリの講座で、「https系のサイトはとりあえず、"OpenSSL::SSL::VERIFY_NONE"しとけば通る」とあって「どういう理屈なんだろ」と思ったことである。

おまけ

じゃあ実際にこのVERIFY_NONEを使わずにやってみようとしたけど、別にまったく難しくない。
FireFoxを例にあげると、
URLの左の鍵マークをクリック→証明書の表示→「セキュリティ」タブの「証明書を表示」→「詳細」タブの「証明書の階層」のところで証明書をクリックして指定し、エクスポートする(階層によって何が違うのかは調べていません。とりあえずテストとしてYAHOOJAPANのサイトのログインを一番上の階層の証明書で試してみたところ、成功した)→エクスポートした証明書ファイル(.crtファイルだと思われる)を実行するスクリプトと同じディレクトリに置き、スクリプト内で

agent.ca_file = "./GlobalSignRootCA.crt"

と指定すれば解決する。
絶対パス指定だと無理だったので注意。ここですごく詰まって、ソウルジェムが濁りました。

ちなみにVERIFY_NONEが結局どういうオプションなのかについてだが、るりま*2から参照すると、

サーバーモードであるかクライアントモードであるかによって意味がことなります。
サーバーモード: クライアントに証明書を要求せず、クライアントは証明書を 送り返しません。
クライアントモード: サーバから受け取った証明書は検証されますが、失敗しても ハンドシェイクは継続します。

また証明書の検証については、IT用語辞典*3より

通常、Webブラウザなどデジタル証明書を利用するソフトウェアには、ソフトウェアの発行元やユーザが信頼できるルート認証局の証明書が同梱されている。Webサイトなどに接続した時に、相手の提示した証明書が信用できるかどうかは、発行元の認証局を調べ、さらにその認証局を認証している上位局を調べ…といった具合に辿っていき、最終的に自分の手元にあるルート証明書に一致するルート認証局にたどり着けば、信用できると確認できる。

とある。先ほどの認証局の階層というのはおそらくこの「認証局辿り」にあたるのだろう。
これに失敗したら通常はエラーを投げるようにして終了するようにしているが、NONEにするとこれを無視して継続するということになる(のかな)。

結局よくわからなかったところ

結局VERIFY_NONEにしたときに通信は暗号化されているのか。今の理解では証明書をおそらく受け取っていない(そのため、わざわざエクスポートして指定してあげなければいけない)ので通信は暗号化されていないように思えるのだが、それは「推奨されない」レベルの警告にとどめておいていいものなのか……
もし通信が暗号化されているとすれば、明示的に証明書ファイルを指定しない時の証明書、公開鍵の扱いはどうなっているのか。

詳しい人、おせーてください。
他、参考URL:
https://www.verisign.co.jp/ssl/https.html
http://d.hatena.ne.jp/shibainu55/20081213/1229243493
http://d.hatena.ne.jp/komiyak/20130508/1367993536

*1:当然これは基本的に第三者機関が発行することになっている。

*2:http://docs.ruby-lang.org/ja/2.0.0/method/OpenSSL=3a=3aSSL/c/VERIFY_NONE.html

*3:http://e-words.jp/w/E383ABE383BCE38388E8AA8DE8A8BCE5B180.html