3.5 ハンドシェイク
この節では、TLSの実際のやりとりについて説明します。
3.5.1 フルハンドシェイク
拡大する
図-1 TLS 1.2フルハンドシェイク TLS_RSA_WITH_AES_128_CBC_SHA
クライアントがサーバに初めて接続するときは、フルハンドシェイクをする必要があります。TLS 1.2でTLS_RSA_WITH_AES_128_CBC_SHAが選ばれると、図-1のようなやりとりになります。
- クライアントは、ClientHelloで暗号スイートの候補を提示します。
- サーバは、TLS_RSA_WITH_AES_128_CBC_SHAを選んだことをServerHelloで伝えます。Certificateには、サーバのRSA証明書が入っています。
- クライアントは、秘密を生成、サーバのRSA公開鍵で暗号化し、ClientKeyExchangeに格納して送ります。その後ChangeCipherSpecを送って、通信路を暗号路に切り替えます。この暗号路は、AESのCBCモードで暗号化されます。暗号路に切り替えた直後に、ハンドシェイクがうまくいった証拠としてFinishedを送ります。また、今後アプリケーションから受け取ったデータも、この暗号路を用いて送られます。図-1の灰色は、暗号路を表現しています。
- サーバは、サーバの秘密鍵を使って秘密を取り出し、クライアントと同様にChangeCipherSpecを送って、通信路を暗号路に切り替えます。
次に、TLS 1.2でTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256が選ばれたときを説明します(図-2)。TLS_RSA_WITH_AES_128_CBC_SHAの場合と異なる点は、次のとおりです。
- ClientHelloを受け取ったサーバは、TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を選択します。そして、ECDHEの使い捨て公開鍵と秘密鍵を生成します。この公開鍵は、ServerKeyExchangeの中に入れて送ります。
- クライアントも、ECDHEの使い捨て公開鍵と秘密鍵を生成します。自分の秘密鍵とサーバの公開鍵から、秘密を生成します。ClientKeyExchangeには、自分の公開鍵を入れて送ります。
- サーバは、自分の秘密鍵とクライアントの公開鍵から秘密を生成します。
フルハンドシェイクは、TLS 1.0、1.1、1.2では変わり映えしません。しかしながら、TLS 1.3のフルハンドシェイクは根本的に再設計されています。なんと言っても、Helloに鍵交換の役割を持たせることで、RTT(Round Trip Time)を1つ減らしているのです。
- クライアントは、ECDHEの使い捨て公開鍵と秘密鍵を作り、ClientHelloのオプションに公開鍵を格納して送ります。
- サーバも、ECDHEの使い捨て公開鍵と秘密鍵を作り、ServerHelloのオプションに格納して送ります。また、ここからすぐ通信路が暗号化されます。サーバの証明書を格納するCertificateやFinishedは、暗号化されて送られます。Finishedを送ったあとは、更に安全な暗号路へと切り替わります。図-3の灰色の濃さの違いは、この暗号路の違いを表現しています。
- クライアントは、現在の暗号路でFinishedを送った後、更に安全な暗号路へと切り替えます。
拡大する
図-2 TLS 1.2フルハンドシェイク TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
拡大する
図-3 TLS 1.3フルハンドシェイク TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
3.5.2 セッションの再開
クライアントとサーバが、以前TLS 1.2でフルハンドシェイクしていれば、そのセッションを再開することで、鍵交換を省略できます。図-4をご覧ください。
- クライアントは、ClientHelloで再開したいセッションのIDを指定します。
- サーバは、指定されたセッションIDに対する状態を保存していれば、それを使って暗号路に切り替えます。
セッションの再開は、公開鍵暗号の重い計算を省略するばかりではなく、RTTも1回減らせます。しかし、この方法ではサーバがセッション情報を保持する必要があります。クライアントの数に比例して、保持すべき状態の数も増えます。サーバの負担が増えるこの方法は、あまりうまいとは言えません。
サーバの負担を減らす方法として、RFC 5077でセッションチケットが定義されています。セッションチケットとは、サーバのみが復号できる暗号化されたセッション情報のことです。セッションチケットを用いると、サーバはセッション情報を保持する必要がなくなります。
TLS 1.2でセッションチケットを使うためには、まずセッションチケットのためのフルハンドシェイクをする必要があります(図-5)。
- クライアントは、ClientHelloの拡張として空のセッションチケットを送り、セッションチケットに対応していることをサーバに知らせます。
- サーバも、空のセッションチケットをServerHelloのオプションに指定することで、セッションチケットに対応していることをクライアントに知らせます。
- サーバは、ChangeCipherSpecを使って暗号路に切り替える直前に、生成したセッションチケットをNewSessionTicketに入れて送ります。
- クライアントは、現在のセッション情報と送られてきたセッションチケットを対応付けて保存します。
拡大する
図-4 TLS 1.2セッション再開
拡大する
図-5 TLS 1.2セッションチケットのためのフルハンドシェイク
次に、TLS 1.2でセッションチケットを使ってセッションを再開する方法を説明します(図-6)。
- クライアントは再開するセッション情報とセッションチケットを取り出し、ClientHelloのオプションにセッションチケットを格納して送ります。
- サーバは、セッションチケットを復号して、セッション情報を得ます。必要であれば、新しいセッション情報をNewSessionTicketで送ります。その後、暗号路に切り替えます。
- クライアントは、前述のセッション情報を使って、暗号路に切り替えます。
TLS 1.3のセッションチケットは、RFC 4297で定義されているPSK(Pre-Shared Key)と統合されています。PSKとは、サーバやクライアントの認証のために、公開鍵ではなく、あらかじめ共有している秘密を用いる方式のことです。TLS 1.3のPSKハンドシェイクをセッションチケットの機能に限って使えば、TLS 1.2のそれとあまり変わりません(図-7)。細かな違いは、次のとおりです。
- フルハンドシェイクの後、サーバがいつでもNewSessionTicketを送信できます。
- TLS 1.3のフルハンドシェイクと同様、暗号路が2回切り替わります。
拡大する
図-6 TLS 1.2セッションチケットを用いたセッションの再開
拡大する
図-7 TLS 1.3セッションチケットを用いたセッションの再開
3.5.3 証明書を用いたクライアント認証
TLSを用いて、あるサーバのあるページにアクセスしている場合を考えましょう。そのページにあるリンクの先は、同じサーバにあるが、証明書を使ったクライアント認証が必要なコンテンツだったとします。
TLS 1.2では、途中で証明書を使ったクライアント認証が必要となった場合、再ネゴシエーションを用います。これは文字通り、ハンドシェイクを再び実行するのです。フルハンドシェイクと違うのは、暗号路の中でハンドシェイクすることです(図-8)。
- サーバは、HelloRequestを送ってクライアントに再ネゴシエーションを要求します。
- クライアントは、ClientHelloを送ります。
- サーバは、ServerHelloを送る際に、CertificateRequestも送ってクライアントの証明書を要求します。
- クライアントは、ClientKeyExchangeを送る際にクライアントの証明書をCertificateに入れて送ります。
再ネゴシエーションの本来の目的は、暗号路をリフレッシュして暗号路の寿命を延ばすことです。証明書を用いたクライアント認証にも使われるのは、CertificateRequestをServerHelloの直後に送らなければならないというTLS 1.2の制約からです。
TLS 1.3では、暗号路のリフレッシュと証明書を用いたクライアント認証を明確に分けます。CertificateRequestは、いつでもサーバからクライアントに送れるようになります(図-9)。
拡大する
図-8 TLS 1.2での証明書を用いたクライアント認証
拡大する
図-9 TLS 1.3での証明書を用いたクライアント認証
3.5.4 0-RTT
TLS 1.3では、ClientHelloを送る際にアプリケーションのデータも暗号化して送る0-RTTというハンドシェイクが検討されています。少し複雑なので今回は説明を割愛します。興味がある方はTLS 1.3のIDを参照してください。