SSL/TLS では暗号化通信を開始する前に、SSL/TLS ハンドシェイクと呼ばれるやりとりを行い、暗号化通信を行うにあたって必要な情報を交換し、暗号化通信を確立します。
SSL/TLS ハンドシェイクにて、クライアントとサーバー間で暗号化に使用する各プロトコルの選定や共通鍵の生成、また証明書を検証して信頼できる相手なのかの検証を行います。
SSL/TLS ハンドシェイクの複雑なので、今回は図付きで丁寧に説明します。
TLS ハンドシェイクのフロー
SSL/TLS ハンドシェイクとは、SSL/TLS 通信の開始時にまずクライアントとサーバー間で通信の暗号化に使用する各アルゴリズムの選定や共通鍵の生成、また証明書による認証など、互いに暗号化通信に必要なパラメーターの取り決めが行われます。
TLS ハンドシェイクの流れは以下の通りです。
- Client Hello
- Server Hello
- (Server Certificate)
- (Server Key Exchange)
- (Certificate Request)
- Server Hello Done
- (Client Certificate)
- Client Key Exchange
- (Certificate Verify)
- Change Cipher Spec
- Finished
- Change Cipher Spec
- Finished
- 暗号化通信開始
※ () で囲まれているメッセージは省略されることがあります。
SSL/TLS ハンドシェイクにおける各フローの詳細を見ていきます。
① Client Hello
クライアントはサーバーに対して暗号化通信の要求を送信します。このメッセージには、以下のような内容が含まれています。
- SSL/TLS プロトコル バージョン
- 現在時刻
- クライアント ランダム
- セッション ID
- 使用できる暗号スイートの一覧
- 使用できる圧縮方法の一覧
暗号化通信を行うためには、クライアントとサーバーの双方が利用できるアルゴリズムを使用する必要があります。
そのため、Client Hello にはクライアントが利用できるプロトコルのバージョンやアルゴリズムのの情報(SSL/TLS バージョン、暗号スイートの一覧、圧縮方法の一覧)を含んでサーバーに提示します。
クライアント ランダムとは、クライアントが生成した乱数です。
クライアント ランダムは、プレマスターシークレットやマスターシークレットを生成するためのパラメーターとなります。
セッション ID は、SSL/TLS 通信のセッションを再開する際に利用されるフィールドです。
SSL/TLS は切断したセッションを再開する機能があります。
この機能は一度確立した TLS セッションに対して ID を割り当てた上でセッション情報をキャッシュし、クライアントは再接続の際にセッション ID を指定することで前回のセッションで接続を回復する機能です。
この機能を利用したい場合、セッション ID を Client Hello に含め接続します。
② Server Hello
サーバーが Client Hello を受けて、SSL/TLS 通信に使用する暗号スイートを決定したり、通信の暗号化に使用する鍵の情報をクライアントに通知します。
このメッセージには、以下の内容が含まれています。
- 使用 SSL/TLS のプロトコル バージョン
- 現在の時刻
- サーバー ランダム
- セッション ID
- 使用する暗号スイート
- 使用する圧縮方法
サーバーでは Client Hello で提示された、クライアントが利用可能な各アルゴリズムの一覧(SSL/TLS バージョン番号、暗号スイート、圧縮方法の一覧)から実際に SSL/TLS 通信に利用するものを選択して、クライアントに通知します。
サーバー ランダムとは、サーバが生成した乱数です。
サーバー ランダムは、プレマスターシークレットやマスターシークレットを生成するためのパラメーターとなります。
③ (Server Certificate)
サーバーはクライアントに対して、身分情報としてサーバー証明書を送信します。
Server Certificate にはサーバー証明書のみだけではなく、サーバー証明書を発行した証明機関の CA 証明書や、さらにその上位の証明機関があればその CA 証明書も含んで送信します。
Server Certificate の証明書リストの先頭にくるのはサーバー証明書である必要があり、さらにその後に発行証明機関の CA 証明書が続きます。(信頼チェーンの降順)
信頼チェーンの末尾にルート証明書を含めることもできますが、ルート証明書はそもそもクライアントが信頼しているルート証明書である必要があります。
仮に送信してもクライアント側では無視されますので、ルート証明書は基本的には送信しても意味がないため省略されるべきとされています。
④ (Server Key Exchange)
共有鍵の鍵交換に必要なパラメーターに関する情報をクライアントに送ります。
どのような情報をクライアントに送信するかは、選択された暗号スイートで指定されている鍵交換の方式によって異なります。
⑤ (Certificate Request)
サーバーがクライアントを認証する必要がある場合に、クライアントに対して認証用の証明書を送るように要求します。
このメッセージには以下の内容が含まれています。
- サーバーが理解できる証明書のタイプ一覧
- サーバーが信頼している証明機関の名前一覧
クライアントから提示される証明書で認証するためには、サーバー自身がクライアント証明書を発行した証明機関を信頼している必要があります。
そのため、証明書の送信要求において、サーバー自身が信頼している証明機関の一覧を提示し、クライアントは一覧にあるいずれかの証明機関から発行された証明書を認証用に提示できるようにします。
認証が不要な場合には省略されます。
⑥ Server Hello Done
Server Hello から始まる一連のめせーじが完了したことをクライアントに通知します。
クライアントはこれを受けて、Server Hello の一覧のメッセージが完了したとみなします。
⑦ (Client Certificate)
サーバーからertificate Request を受けた場合、クライアントの認証用の証明書を送信します。
そのメッセージには、クライアント証明書を発行した証明機関の CA 証明書や、さらにその上位の証明機関があればその CA 証明書も含んで送信します。
Certificate Request が送信されていない場合には、省略されます。
⑧ Client Key Exchange
共有鍵の鍵交換に必要なパラメーターに関する情報をクライアントに送ります。
どのような情報をクライアントに送信するかは、選択された暗号スイートで指定されている鍵交換の方式によって異なります。
クライアントとサーバーは、ここまで交換した情報(Server Key Exchange、Client Key Exchange) の情報をもって、それぞれで共有鍵を作成します。
⑨ (Certificate Verify)
クライアントとサーバーが認証用に送信した証明書について、本当に自身がその証明書の持ち主であることを示す情報をサーバーに送信します。
クライアント証明書に紐づく秘密鍵を用いて、SSL/TLS ハンドシェイクでやり取りにしたメッセージのデジタル署名を生成してサーバーに通知します。
Certificate Request が送信されていない場合は省略されます。
⑩ Change Cipher Spec
クライアントが暗号化通信に必要な準備が完了したことを示すメッセージです。
ここまでの SSL/TLS ハンドシェイクの通信で、サーバーとクライアントはプレマスターシークレットの情報を共有できたことになります。
クライアントとサーバーは、それぞれ TLS ハンドシェイクの中でやり取りされたクライアント ランダム、サーバー ランダム、プレマスターシークレットの情報からマスターシークレットを作成します。
そのマスターシークレットから、暗号化通信に用いるための共通鍵(セッション鍵とも呼ばれます)などを生成します。
これで暗号化通信の準備は完了したため暗号化通信を開始できるということを相手に通知するためのメッセージとして Change Cipher Spec を送信します。
⑪ Finished
クライアントから SSL/TLS ハンドシェイクのメッセージを終了することを宣言するメッセージを送信します。
Finished メッセージには verify_data というフィールドがあり、これまでの TLS ハンドシェイクの全通信のデータのハッシュ値を算出し、その値とマスターシークレットを組み合わせて計算した結果を格納します。
この値より、SSL/TLS ハンドシェイクの一連のデータを保持していること、また、共通のマスターシークレットを保持していることを示すことができます。
まあた、MAC により、verify_data の完全性も保証されております。
すなわち、第三者の攻撃者はマスターシークレットを知りえないので、verify_data の値を改ざんすることはできず、TLS 通信を乗っ取ることはできません。
Finished メッセージは暗号化されているため、verify_data をパケットから見ることはできません。
⑫ Change Cipher Spec
サーバーが暗号化通信に必要な準備が完了したことを示すメッセージを送信します。
同様にこれ以降は暗号化の処理に切り替えるkとおを相手に宣言するためのメッセージとなります。
クライアントと同様に、プレマスターシークレット、マスターシークレットを生成して暗号化通信に使用する共通鍵を生成します。
⑬ Finished
サーバーからハンドシェイク メッセージが終了することを宣言するメッセージを送信します。
サーバー側も同様に Finished メッセージに verify_data を含めて送信します。
verify_data の値の算出方式は同じであり、クライアントから送信された値と同じ値が入ります。
同じく、Finished メッセージは暗号化されているため、verify_data をパケットから見ることはできません。
プレマスターシークレット/マスターシークレット
SSL/TLS 通信において、通信相手とデータを暗号化して通信するためには、クライアントとサーバーで共通鍵を共有する必要があります。
共通鍵の共有は SSL/TLS ハンドシェイクにて行われる鍵交換にて行われますが、鍵交換において実際に交換されるのは共通鍵そのものではありません。
RSA の鍵交換において実際に交換されるのは共通鍵そのものではなく、プレマスターシークレットとよばれる乱数のデータとなり、DHE や ECDHE の鍵交換においてはプレマスターシークレットを生成するためのデータとなります。
共通鍵の生成に使用されるプレマスターシークレット、マスターシークレットは以下の通りです。
プレマスターシークレットとは
プレマスターシークレットとは、後からマスターシークレットを生成するための元データです。
SSL/TLS ハンドシェイクにて選択された鍵交換の方式が RSA の場合は、クライアントで生成した乱数をプレマスターシークレットとしClient Key Exchange にサーバー証明書の公開鍵で暗号化して送信します。
一方、SSL/TLS ハンドシェイクにて選択された鍵交換の方式が Diffie-Hellman 方式(DHE、ECDHE 等) の場合は、Server Key Exchange、Client Key Exchange メッセージにプレマスターシークレットの生成に必要なデータを交換し、そのデータをもとにプレマスターシークレットのデータを算出します。
マスターシークレットとは
マスターシークレットとは、暗号化通信を行うにあたって利用される以下の要素を生成するための元データとなります。
- 暗号化通信に利用する鍵(通信対象のアプリケーション データの暗号化に使用する鍵)
- メッセージ認証コード (MAC) に使用する鍵
- CBC モードなどで利用される初期化ベクトル
SSL/TLS ハンドシェイクの中でやり取りした以下の情報をもとにマスターシークレット(48 バイト) を生成します。
サーバーとクライアントは、各自マスターシークレットの生成の処理を行いますが、共通のデータをもとに同じ計算を行うので、結果として同じデータ(マスターシークレット)が生成されます。
マスターシークレットのデータは、クライアント ランダムとサーバー ランダムのデータを組み合わせて、以下が生成されます。
- クライアント ランダム
- サーバー ランダム
- プレマスターシークレット
SSL/TLS ハンドシェイクの過程で柵瀬いされる各情報のイメージ配下の通りです。
※ 補足:EMS (Extended Master Secret) とは
EMS は新しいマスターシークレットの算出方法です。
SSL/TLS 通信を行うクライアントとサーバーの両方が EMS の機能をサポートしている場合、従来の算出方法とは異なる方式を利用します。
従来のマスター シークレットの算出であれば、上記の通り、クライアントランダム、サーバー ランダム、プレマスターシークレットの3つのパラメーターから算出しますが、EMS の機能が有効の場合、 SSL/TLS ハンドシェイクのフローにおける Client Hello から Client Key Exchange までの平文の箇所のデータ全てのハッシュ値を算出し、そのハッシュ値とプレマスターシークレットの2つのパラメータから算出します。
従来のマスター シークレットの算出方式では、Triple Handshake 攻撃を受ける恐れがあり、EMS はその攻撃に対する対策として考案されています。
SSL/TLS セッション再開
同じ通信相手と複数回にわたり SSL/TLS 通信を行う場合、SSL/TLS 通信を確立する度に SSL/TLS ハンドシェイクを行うのでは、通信における処理コストが無駄にかかってしまいます。
(証明書の検証や鍵交換などの処理は CPU 負荷もかかる処理となります)
そのため、同じ通信相手と SSL/TLS 通信を行う場合、前回の SSL/TLS 通信確立時のセッション情報をキャッシュとして保持し、セッション情報を再利用して SSL/TLS 通信を確立することができます。
これを SSL/TLS セッション再開 (SSL/TLS session resumption) と呼びますが、セッション再開時の SSL/TLS ハンドシェイクの動作では、SSL/TLS ハンドシェイク時に通信相手が正当なものであるかを確認する証明書の検証の処理や鍵交換の処理が省略されます。
TLS セッション再開を実現する手法としては、2つ存在します。
Session ID
サーバーがセッション情報のキャッシュを保持し、セッションを識別するための ID を生成し、Server Hello に含めてクライアントに通知します。
クライアントは次回の TLS 通信の再開時に、セッション ID を Client Hello に入れて接続します。
サーバーはセッション ID に紐づくキャッシュを利用して SSL/TLS セッションを再開させます。
Session Ticket
サーバーがセッション情報を暗号化したデータを Session Ticket としてクライアントに渡して、サーバー側ではセッション情報に関するキャッシュは保持しません。
クライアントは次回の SSL/TLS 通信の再開時に、Session Ticket を Client Hello に入れて接続要求を行います。
サーバーは Session Ticket を復号してセッション情報を取得し、SSL/TLS セッションを再開させます。
SSL/TLS セッション再開は機能は、SSL/TLS ハンドシェイク字の負荷を軽減するためであり、通常の SSL/TLS ハンドシェイクで行われるパケットのやり取りは一部省略されます。
通常の TLS ハンドシェイクでは全てのやり取りが行われるため、SSL/TLS のセッション再開のハンドシェイクと区別するため “フルハンドシェイク” という表現を使うこともあります。
SSL/TLS セッション再開の時の SSL/TLS ハンドシェイクは以下の通りです。
SSL/TLS セッション再開時には、薄いグレーの SSL/TLS ハンドシェイクのパケットについては省略されます。