SSL/TLSとは
暗号化・改ざん検知・認証機能を提供するプロトコル。
TLSはSSLの次世代規格で、現在使われているのはほとんどTLSだが、今でもそのままSSLと呼ばれることが多い。本記事もSSLで統一している。
SSLハンドシェイクとは
SSL/TLS通信でサーバーを認証し、「公開鍵」と「秘密鍵」を使って「共通鍵」を共有し、安全な通信を行うための過程。
盗聴、改ざん、なりすましから守りながら、クライアントとサーバーで共通鍵を持つことが目的になる。
これを実現するために
- どのような暗号スイートを使うか
- 暗号化に使う鍵
をクライアントとサーバーで共有しておく必要がある
メリット
- 通信先のサーバーが本当に通信したいサーバーなのかを確かめられる
- 証明書を活用する
- 共通鍵による暗号化でセキュアな通信を実現できる
- 公開鍵暗号方式よりも速い
- セッション情報をキャッシュとして保存できる
- 重い処理をスキップできる
- 署名によって改ざんの検知できる
ざっくりしたフロー
- クライアントはサーバーに必要な情報を送る
- サーバーはクライアントにサーバー証明書などの情報を送る
- クライアントは送られてきた情報を検証してサーバーを確認
- 暗号化に使う鍵を共有
- 以降鍵を使ったセキュアな通信ができるようになる
SSLハンドシェイクのフロー
Client Hello(クライアント → サーバー)
これから始めるハンドシェイクのために必要な情報をサーバーに送信する。
具体的には
- 使用できるSSL/TLSプロトコルのバージョン
- 現在時刻
- クライアントランダム(乱数)
- (セッションID)
- 使用できる暗号スイートの一覧
- 使用できる圧縮方法の一覧
を送ることができる。
暗号スイート一覧はクライアントが希望する順に並べる。
Server Hello(クライアント ← サーバー)
ハンドシェイクのために必要な情報をクライアントに送信する。
具体的には
- 使用するSSL/TLSプロトコルのバージョン
- 現在時刻
- サーバーランダム(乱数)
- セッションID
- 使用する暗号スイートの一覧
- 使用する圧縮方法の一覧
を送ることができる。
Clietn Helloで送られてきた暗号スイート一覧の中に利用できるものがない場合、ハンドシェイクは失敗する。
Server Certificate(クライアント ← サーバー)
サーバーハローと同時に行われる。
認証局に発行してもらったサーバー証明書をクライアントに送る。この時、クライアントは証明書の検証に必要となる中間証明書やルート証明書も送る。ただし、ルート証明書は省略されることがある。
クライアントがサーバー証明書を求めていない場合、このメッセージは省略される。
サーバー証明書の確認(クライアント)
クライアントは認証局がサーバー証明書に添付した署名を認証局の公開鍵を使って復号。復号したものと、送られてきたサーバー証明書をハッシュ化したものが同じなら、正しい通信相手のサーバーであるとする。
Server Key Exchange(クライアント ← サーバー)
共有鍵の鍵交換に必要なパラメーターに関する情報をクライアントに送る。
これはServer Certificateが省略された場合や、Server Certificateで送信したSSLサーバー証明書に公開鍵が含まれていない場合に必要になる。
Server Certificateが省略されることは稀らしいので、このメッセージが必要になることはあまりない。
Certificate Request(クライアント ← サーバー)
サーバーがクライアント証明書を求めるメッセージをクライアントに送信する。
メッセージにはサーバーが信頼する認証局の一覧を送る(送らないこともできる)。
このメッセージは省略可能。
Server Hello Done(クライアント ← サーバー)
Server Helloに関する情報を送り終えたことを伝える。
Client Certificate(クライアント → サーバー)(オプション)
指定された認証局に認証してもらったクライアント証明書をサーバーに送信する。適切な証明書がない場合は証明書を含めずにこのメッセージを送信する。
Certificate Requestが送られてきた場合のみ実行される。
Client Key Exchange(クライアント → サーバー)
クライアントは共通鍵の元になるランダムな値(プリマスターシークレット)を生成。これをサーバー証明書についている公開鍵で暗号化してサーバーに送る。
サーバーは送られてきた値を対となる秘密鍵で復号。共有鍵の元になる値(プリマスターシークレット)を取得する。
Client Verify(クライアント → サーバー)
クライアントはSever Helloで受信した情報からデジタル署名を作成し、サーバーに送信する。
サーバーは証明書の公開鍵を使って署名の検証を行う。
クライアントが証明書をサーバーに送った時のみ実行される。
Change Cipher Spec(クライアント → サーバー)
クライアントはクライアントランダムとサーバーランダム、プリマスターシークレットを使ってマスターシークレットを生成する。さらにマスターシークレットを使って、サーバーとの通信に使う共通鍵を生成する。
クライアントは、暗号化通信に必要な準備が完了したことを示すメッセージをサーバーに送信する。
(厳密にはこのメッセージはハンドシェイクメッセージではない、らしい)
Finished(クライアント → サーバー)
ハンドシェイクの終わりを示すためのメッセージをサーバーに送信する。
内容にこのメッセージより前のハンドシェイクで送受信したすべてのデータのHMACを含めて送信する。サーバーはこれを検証することにより、ハンドシェイクが改ざんされておらず、クライアントが秘密鍵を持っていることを確認できる。
Change Cipher Spec(クライアント ← サーバー)
サーバーはクライアントから受信した、暗号化されたプリマスターシークレットを復号し、クライアントランダム、サーバーランダムを使ってマスターシークレットを生成。さらにこれを使って共通鍵を生成する。ここで生成された共通鍵はクライアントが生成したものと同じになる。
サーバーは、暗号化通信に必要な準備が完了したことを示すメッセージをサーバーに送信する。
(厳密にはこのメッセージはハンドシェイクメッセージではない、らしい)
Finished(クライアント ← サーバー)
ハンドシェイクの終わりを示すためのメッセージをクライアントに送信する。
内容にこのメッセージより前のハンドシェイクで送受信したすべてのデータのHMACを含めて送信する。クライアントはこれを検証することでこれまでのメッセージに改ざんがないこと、お互いに暗号化ようの共通鍵を持っていることを確認できる
これ以降は共通鍵を使ってセキュアな通信が可能になる
まとめ
おまけ : セッションを使うと・・・
- Client Hello
- Server Hello
- ChangeCipherSpec
- Finished
- ChangeCipherSpec
- Finished
だけて済む。
証明書の確認など、CPUに負担がかかる処理を省くことができる! 大変ありがたい。
最後に
ざっくりとした理解しかしていなかったが、改めて調べてみると細かいやりとりがあることがわかった。
本記事の内容に誤りがある場合はご指摘いただけるとありがたいです。
参考
SSL/TLSサーバ証明書、コードサイニング証明書 | 国内最安値DigiCert(デジサート)正規代理店
サーバ証明書・SSL/TLS通信/実際に認証局に申請してみた【高校情報1・情報処理技術者】OpenSSLでCSRを生成
TLS・SSLハンドシェイクの 仕組みは? | Cloudflare
Wireshark で TLS ハンドシェイクの流れを見てみる - Qiita
SSL/TLSのハンドシェイクってどんなの? - Qiita
【PKI 応用】SSL/TLS ハンドシェイクをわかりやすく図解 | ねこまるの AD フリーク
SSL/TLS徹底入門: わかりやすい図解解説 インターネット技術