SSL証明書についてのメモです。
いつも組み込むとき細部を忘れて何やってるのか判らなくなるので勉強し直し・・・。
間違ってるところは指摘してもらえると助かります。
SSLの目的
情報を暗号化して送信する。
送信の暗号化とhttps
成り立ち
送信される情報は盗聴可能、よってパスワードや個人情報を平文で送信するのはまずい。
しかしhttpプロトコルは平文でしか送信できない。その為別の仕組みが必要になった。
httpsプロトコル
httpではなくhttpsを利用する事で情報は暗号化されて送信する事が出来る。
httpsの仕組み(基本)
基本的には共通鍵暗号でやり取りする。
- クライアントが共通鍵を生成して送信
- サーバーが共通鍵を受け取る
- 続けて共通鍵で暗号化したリクエストを送信
- サーバーは先ほど送られた共通鍵でリクエストを複合して受信
しかしこの流れ、盗聴の為に暗号化するのにその暗号化の為の共通鍵を盗聴の危険のある通信路に流して送信するなんて何とも馬鹿な話だと思わないだろうか。
よって当然平文では送信しない。
送信する共通鍵は更に公開鍵暗号によって暗号化する事になる。
httpsの仕組み(共通鍵を公開鍵暗号で暗号化する)
公開鍵暗号を利用する場合は次のような流れになる
- クライアントが共通鍵を生成して「公開鍵で暗号化して」送信
- サーバーが「秘密鍵で複合化して」共通鍵を受け取る
- 続けて共通鍵で暗号化したリクエストを送信
- サーバーは先ほど送られた共通鍵でリクエストを複合して受信
問題はこの公開鍵はどこにあるの?という話。
これはサーバーから送ってくる。よって実際には次のような流れになる。
- サーバーはあらかじめSSL通信用の公開鍵、秘密鍵を生成しておく
- 通信が発生したら先にサーバーが公開鍵をクライアントに送信
- クライアントは共通鍵を生成して、サーバーから送信された公開鍵で暗号化して送信
- サーバーが秘密鍵で複合化して共通鍵を受け取る
- 続けて共通鍵で暗号化したリクエストを送信
- サーバーは先ほど送られた共通鍵でリクエストを複合して受信
しかし公開鍵をそのままサーバーが送ってくる仕様の場合次のようなケースで問題になる。
- サーバーはあらかじめSSL通信用の公開鍵、秘密鍵を生成しておく
このとき盗聴者も自作の公開鍵、秘密鍵を生成しておく。 - 通信が開始されたら先にサーバーが公開鍵をクライアントに送信。
この送信された公開鍵を盗聴者が[1]で自作した公開鍵にすり替える。後程このサーバー公開鍵は必要になるのでここで保存もしておく。 - クライアントは共通鍵を生成して、サーバーから送信されたと思ってる公開鍵で暗号化して送信。
しかしここで利用している鍵は実際には盗聴者がすり替えた鍵となる。 - 盗聴者がこのサーバーに向けて送信された共通鍵を盗聴し、対になる自作の秘密鍵で複合化。これで共通鍵を盗み見る事が出来た。
しかしこのままサーバーに送信されるとサーバーは自分の秘密鍵で複合できない為問題が発生したことが判ってしまう。そこで[2]でサーバーから送信された公開鍵で再度共通鍵を複合化して送信された情報をすり替えておく - 何も知らないサーバーは秘密鍵で複合化して共通鍵を受け取る
- 続けてクライアントは共通鍵で暗号化したリクエストを送信
しかし盗聴者は共通鍵を知っているのでリクエストを盗み見る事が出来てしまう - サーバーは先ほど送られた共通鍵でリクエストを複合して受信
これでは意味が無いだろうという事で、クライアントは送信されてきた公開鍵が本当に正しいものなのか?盗聴者が作成した偽物ではないのか?ってのを確認しなければならない。
電子署名による正当性チェック
送信されてきた公開鍵はサーバーが送ってきたのか?盗聴者が送ってきたのか?を判断するのには電子署名の仕組みが利用されている。
電子署名とは
電子署名は決してSSL通信の為の仕組みではない。
例えばゲームのセーブデータがあったとする。
このセーブデータが改ざんされていないかを確認する場合てっとり早いのはハッシュをとる方法。
- セーブデータのハッシュを取る(SHA256とかで)
- セーブデータが仮に改竄されたとする
- [1]で取ったハッシュと、セーブデータのハッシュを再度取り直して完全に一致しなかったらデータは改竄されていると判定できる
ここで[1]で取ったハッシュが署名の事。
ただしハッシュを取るといっても単純にSHA256でハッシュを取ったのではセーブデータを改竄した後に改竄した人がその改竄後のセーブデータで再度SHA256でハッシュを取り直せば[3]で一致したと判定されてしまう。
これでは何の意味も無いので[1]で取ったハッシュは更に秘密鍵で暗号化する。
- セーブデータのハッシュを取り、秘密鍵で暗号化したバイナリを「署名」とする
- セーブデータのハッシュを取ったバイナリデータと、公開鍵で「署名」を複合化して出てきたバイナリデータが一致したらセーブデータは改竄されていないものとする
このセーブデータを改竄するには秘密鍵を知っている必要があるが、当然ゲームの製作者しか秘密鍵は知らないはずなのでセーブデータは秘密鍵がばれさえしなければ改竄できない。
これが署名の仕組みで、これを公開鍵にも利用しようという話。
署名付の公開鍵
サーバーは公開鍵を送信する際に「署名」も一緒に付ける。
ここで別の鍵が出てくるのが判り辛さが増している要因。ポイントは公開鍵の署名を取る際に利用している鍵と共通鍵を暗号化するのに使っている鍵が違うという事なんだけど、これを文字に起こすと判り辛い事がこの上ない。
署名付の公開鍵を利用する場合の通信の順序
再掲となるが署名無しの場合は次の順序で通信を行う
- サーバーはあらかじめSSL通信用の公開鍵、秘密鍵を生成しておく
- 通信が発生したら先にサーバーが公開鍵をクライアントに送信
- クライアントは共通鍵を生成して、サーバーから送信された公開鍵で暗号化して送信
- サーバーが秘密鍵で複合化して共通鍵を受け取る
- 続けて共通鍵で暗号化したリクエストを送信
- サーバーは先ほど送られた共通鍵でリクエストを複合して受信
しかしこれではサーバーから送信された公開鍵が盗聴者によってすり替えられた公開鍵に変わっている可能性がある。
そこで公開鍵に対して署名を付ける。その場合次のような流れになる。
- 先ず大前提として「署名用の公開鍵、秘密鍵」というのが存在し、サーバーは「署名用の秘密鍵」を持っており、クライアントは自分のPC内に「署名用の公開鍵」が存在するものとする。
- サーバーは通信前に「SSL通信用の公開鍵」のハッシュをSHA256等のハッシュアルゴリズムを用いて取り、そのハッシュに対して[1]の「署名用の秘密鍵」で暗号化。この暗号化された情報を「署名」とする。
- 通信が発生したら先にサーバーは「公開鍵」と「3で作成した署名」を一緒にクライアントに送信
- クライアントは公開鍵と署名を受け取り、先に「自分のPC内にある署名用の公開鍵」を用いて署名を複合化。更に公開鍵のハッシュもクライアント側がSHA256等のハッシュアルゴリズムを用いて生成し、署名と公開鍵のハッシュが一致するかを確認。もしも一致していないなら盗聴者によって公開鍵がすり替わっていないものとする。
- 公開鍵の正当性が確認できたので、共通鍵を生成して、サーバーから送信された公開鍵で暗号化して送信
- サーバーが秘密鍵で複合化して共通鍵を受け取る
- 続けて共通鍵で暗号化したリクエストを送信
- サーバーは先ほど送られた共通鍵でリクエストを複合して受信
これなら盗聴者が公開鍵を入れ替えるには最低でも「署名用の秘密鍵」が必要になる。
ところでこの署名用の秘密鍵はどこにあるのだろう。
この鍵が認証局にあるのだ。
認証局
認証局は「署名用の秘密鍵」を持っており、それと対になる公開鍵はクライアントのPC内にデフォルトでインストールされている。
公開鍵の署名を作成する場合、先に認証局に「この公開鍵の署名を作ってよ」と依頼し、認証曲は公開鍵用の署名を発行してくれる。
後はサーバー管理者は発行された署名を通信時に送信すれば良い。
こうすれば公開鍵をすり替えても署名が一致しなければ公開鍵がすり替わっていることが判る。
署名の穴とそれを埋める為のサーバー証明書
サーバーの公開鍵の署名を作成する場合、署名作成に必要な「署名用秘密鍵」は認証局に存在するので認証局に依頼するしかない。
しかし単純に「この公開鍵の署名を作ってよ」と誰でも言えてしまうと盗聴者が自作の公開鍵を作って認証局に依頼すれば署名が作れてしまう。
こうなると次のケースで問題になる。
- SSL通信が開始されたのでサーバーが公開鍵と署名を送信する
- ここで盗聴者が自作の公開鍵と署名に入れ替える
- クライアントは公開鍵と署名が一致したので問題ないものとする
ようは公開鍵も署名も丸ごとすり替えられると意味が無いという事。
そこで「サーバー公開鍵とドメイン名、それ以外にもサーバーの情報が書かれた情報ファイル」と、この情報ファイルに対する署名を送信する事になる。
この情報ファイルの事をサーバー証明書と呼ぶ。
・サーバーのドメイン名
・サーバーの公開鍵
・署名を発行した認証局の情報
・それ以外にも国とか名前とかのサーバーの情報とか色々...
ABVNIKSASFKADUJAVKMAWEIKJHFAKLSMN
AFSJIAOGJLAFOAJFAKJFWAOFAWGAGOAJG
ASGHIAWSGFKJALWGKJALSFOJAOVJAMLVJ
SDGHJNDLFJDLHJKS+LOFKJA+OFKJA+FSL
DSRIHOGSFDJA+FOJAFK+SAHGLOAIWGJLA
FKASJHFLAVaDGDAGFSKSJVNLAKFJADFAS
...
認証局に署名を付けてもらう場合このサーバー証明書に対する署名を付ける。
認証局側ではこのサーバー証明書と署名、もといドメイン名と署名を対で管理しておけば同じドメイン名に対する署名は作れなくなる。
クライアント側では送信されてきたサーバー証明書のドメイン名が現在通信しているサーバーのものと確認するので、盗聴者はどうしても通信しようとしているサーバーのドメイン名でサーバー証明書を作り署名作成を認証局に依頼しなければならない。
しかし既にそのドメイン名では署名が作られているので署名を作ってもらえない。
これで盗聴者は遂に諦めて泣き寝入りするしかなくなるわけだ。
共通鍵の必要性とサーバーからクライアントへの暗号化通信
共通鍵暗号は必要ない??
例えばクライアントからサーバーに対してのみの通信を行うとする。
クライアントはサーバーから送信されてきた公開鍵を使って単純にデータを暗号化して送信すれば良い。
そうすると当然「あれ、共通鍵で暗号化する必要なくね?」って話になる。
公開鍵暗号は一方通行である事を思い出す
そもそも何故共通鍵で暗号化するのか?
それは公開鍵方式自体が基本的には一方通行の通信だからだ。
クライアントからサーバーへの通信のみなら当然クライアントが公開鍵を持っててサーバーが秘密鍵を持ってれば暗号化通信は可能。
しかしサーバーからクライアントに暗号化データを送信する場合は別だ。
クライアントの公開鍵には署名が無いから盗聴者がすり替えてもサーバーは今通信しているクライアントが誰なのか判らない。
文章で書くとややこしいがこの記事のクライアントとサーバーを入れ替えて読んでみれば判るはず。
サーバーの公開鍵の署名にはドメイン名が入っており、クライアントはそのドメイン名が変わっていなければ「今通信しようとしているサーバーの公開鍵だ!」と判る。
ようはこれをひっくり返す。
クライアントの公開鍵の署名には「何か」が入っており、サーバーはその「何か」を確認して「今通信しようとしているクライアントの公開鍵だ!」と判る。そんな「何か」なんて存在しないので公開鍵暗号方式だけではサーバーからクライアントへ暗号データを渡すのは無理なのだ。
そこで遂に共通鍵暗号の必要性が明らかになる。
サーバーからクライアントへ情報を渡す場合共通鍵で暗号化すれば良い
サーバーとクライアントが共通鍵で暗号化通信が出来ればお互いに暗号化通信が行える。
しかし問題があった。盗聴者によって共通鍵が盗聴されると通信内容がばれてしまう。
そもそも公開鍵暗号が出てきたのは「そこでこの共通鍵を渡す為に公開鍵暗号と署名を利用してサーバーに対して共通鍵を渡す為」なのだ。
一度でも誰にもばれずにサーバーとクライアントで共通鍵を持つことに成功すれば、後はサーバーもクライアントも好きなように暗号化通信が行えるようになる。
加えて共通鍵暗号は公開鍵暗号より高速というメリットも同時にえられて一石二鳥。
こうして暗号化通信ができるようになった、良い話だなぁ・・・
...
以上かな・・・。
書いててよく判らなくなってきたし疲れたのでまた後で更新。
というか実際にやってみないと細部まで良く判らない。上記を踏まえて証明書と署名の申請、実際にサーバーに入れてちゃんと動作するか確認してまたこの資料を更新し直したい。