Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save 261shimizu/c46580759683e0c2a0a2b118a480fb31 to your computer and use it in GitHub Desktop.
Save 261shimizu/c46580759683e0c2a0a2b118a480fb31 to your computer and use it in GitHub Desktop.
ディジタル署名とSSLサーバ証明書

そもそも


ディジタル署名とか、SSLサーバ証明書とか、
出てくる度に忘れていて調べ直しているので、いい加減まとめる。

ディジタル署名


信頼させたい内容(メッセージ)があり、
これをハッシュ関数を使ってハッシュ化する。
ハッシュ化したもの(ダイジェスト)を自分の秘密鍵で暗号化する(ディジタル署名)。

メッセージとディジタル署名を一緒に相手に送る。

相手の公開鍵でディジタル署名を復号し、送られてきたダイジェストを得る。
別にメッセージを受信側でハッシュ関数を使ってハッシュ化し、ダイジェストを得る。

2つのダイジェストを比べて(検証)、一致すれば真に送信元が正しいこと、改ざんされていないことが保証できる。

SSLサーバ証明書


https化の流れが~とよく話題になる。
私が経験したことがあるのはドメイン認証によるサーバ証明書の取得なので以下その前提。

以下、脇道に逸れる。

http通信は、TCPハンドシェイクを最初に行う。  
https通信だと、TCPハンドシェイクのあとにSSLハンドシェイクが行われる。  
(更に脇道にそれると、この段階でSNI:Server Name Indicate によってSSLでのNameVirtualHostの使用が可能になる)  
(また、TCP通信はリクエスト1つにつき1回なので、例えば画像ファイルが多いページとかだと画像を読み込む度にTCP通信が行われてオーバーヘッドが大きい=TCPハンドシェイク→実際のやりとり→TCP接続断。これを解消するのがKeepAlive設定。1つのTCP通信で複数のリクエストを扱えるようにする。)  
(更に更に脇道にそれると、KeepAliveと少しだけ似ているのがFastCGI。CGIプロセスも処理が終了すると消滅するが、FastCGIはCGIプロセスを永続的あるいは一定期間可動させておくことで、プロセス生成と消滅のオーバーヘッドをなくすことで処理速度が向上する。nginxではFastCGIプロセス機能は無いが、FastCGIサーバへのプロキシとしては使える)  

SSLハンドシェイクで、サーバ証明書の送付(中間CA証明書も一緒。更に公開鍵も一緒)や、鍵交換、クライアント証明書の送付がやりとりされる。  
SSLハンドシェイクのざっくりとした流れは以下  
挨拶→サーバ証明書・中間CA証明書の送信→共通鍵配布→クライアント証明書の送付→暗号通信開始  

まず、サーバ証明書のざっくりとした説明。

サーバ証明書は、認証局(CA)が依頼(CSR)を受けて発行するもので、
何を証明しているかは種類によって異なる。
ドメイン認証であれば、証明書の持ち主=ドメインの持ち主であることを証明してくれるし、
もっとちゃんとしたやつだとこれに加えて会社の実在証明とかもある。

例えばドメイン認証のサーバ証明書がほしければ、
CSR(証明書署名要求)を作成する。
これには国や地域、FQDNなどのサーバの情報が含まれている。
もっと言えば、サーバの情報をサーバの秘密鍵で暗号化したものだと思っているけど、あっているか自信が無い。
ただ、CSRは秘密鍵から作られることは確か。

これをCAに提出する際、サーバの公開鍵も一緒に提出する(そもそもCSRに公開鍵が含まれている)。
CAでは公開鍵を使って復号、
更に真にドメインの持ち主であることを確かめる為、
DNSにTXTレコードを追加させたり、そのドメインのページに文字列を埋め込ませたり、
ドメインの管理者のメールアドレスに確認メールを送ったりする。

こうやって申請者=ドメインの持ち主であることを確認できたら、
CSRに署名を行う(サーバ証明書の発行)。
これには期日や証明したCAの情報が含まれている。よって公開鍵も含まれる。

もう少し細かく言うと、CAでDBを持っていて、そのDBに申請者の情報を登録する模様。
また、最近はルートCAが証明書を発行するのではなく、中間証明書が発行する模様。
CSR→中間証明書でサーバ証明書作成→それをルートCAによって署名

次に、サーバ証明書の検証の流れ。

上記のように、クライアントからWebサーバにアクセスすると、
上述のようにSSLハンドシェイク内でサーバからクライアントにサーバ証明書(公開鍵含む)、中間証明書(公開鍵含む)が送付される。

それらを受け取ったクライアントは、
まずサーバ証明書の有効期限を確認する。
期限内であることを確認できれば、次に証明書のチェーン(中間証明書→中間証明書→ルート証明書的な。)
をさかのぼり、ルート証明書を確認する。
ブラウザには予めルート証明書のリストが存在するので、これと突き合わせる。

信頼できるルート証明書に基づいて発行されたサーバ証明書であることが確認できたら、
CAの公開鍵を使って署名を検証する。
署名が検証できたら、 信頼のチェーン が成立し、SSLサーバ証明書の記載内容が信頼できる。

クライアントはSSLサーバ証明書に記載されているホスト名と、実際に接続しているホスト名が一致するか確認し、
確認できたらサーバ証明書の検証を終了する。

長々書いたけど、分かりづらい。
もう少し簡単にまとめる

CAは、サーバの秘密鍵から作ったCSR(公開鍵含む)をハッシュ化しダイジェスト作成。
そのダイジェストをCAの秘密鍵で暗号化して署名。

サーバ証明書=CSR(公開鍵含む)+CAの署名
更にCAは自分の証明書をサーバに送る。

それらの証明書をサーバが受け取ったら証明書と秘密鍵をhttpd.confに記載。

クライアントからのアクセス時にサーバ証明書とCAの証明書を送付。

CAの証明書(ここではいきなりRootとする)をブラウザのリストと突き合わせ。

Root証明書が確認できたら、

RootCAの公開鍵でサーバ証明書の署名を復号し、別に自分でCSRをハッシュ化しでダイジェストとしたものと突き合わせ。

これにより、真にルートCAで署名されたことが確認できるので、真のサーバ証明書であることがわかる。  

このあとホスト名を検証して終了。


更に、クライアントは、プリマスタシークレットを作成しサーバの公開鍵で暗号化してサーバに送付。  


サーバは自分の秘密鍵でプリマスタシークレットを復号。

合意された暗号方式を使用して、サーバとクライアントの両者がプリマスタシークレットからマスタシークレットを作成。  
互いに作ったマスタシークレットは同一となるはずなので、  
そのマスタシークレットから共通鍵を互いに生成。これも同一となる。  

以降は共通鍵によって暗号化される。  

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment