TLS 1.3 ハンドシェイク — 1 往復で通信路を作る
ClientHello から Finished まで、鍵合意・証明書・署名・改ざん検知が 1 往復に組み上がる流れを追い、SNI と 1-RTT の意味をつかむ。
部品はそろった — あとは組み上げるだけ
ここまでの章で、必要な部品はすべて手に入りました。鍵合意(第 3 章)で盗聴されている経路の上に共有秘密を作り、証明書と署名(第 2・4 章)で相手が本物であることを確かめ、共通鍵暗号と改ざん検知(第 2 章)で本文を守る。TLS 1.3 のハンドシェイクは、これらをたった 1 往復に組み上げる手順です。
この章では「次に何が起きるか」を丸暗記するのではなく、各メッセージを見るたびにどの章の道具が、どの問いに答えるために登場したかを確認しながら進みます。
TLS 1.3 ハンドシェイクの全体像。ServerHello の時点で鍵合意が完了し、以降のメッセージ(グレーの領域)はすでに暗号化されています。
行き: ClientHello — 名乗りと公開値を一度に
クライアントが最初に送る ClientHello には、次が載っています。
- 対応できる TLS バージョンと暗号方式の候補 — 「私はこれらが話せます」
- key_share = DH(ECDHE)の公開値 — 第 3 章の「A」を初手で同梱
- SNI(Server Name Indication) — 接続したいホスト名。1 つの IP で複数ドメインを運用するサーバが、どの証明書を出すか選ぶために必要
TLS 1.2 までは「方式を合意する往復」と「鍵を交換する往復」が別で、合計 2 往復かかっていました。TLS 1.3 は公開値を初手に同梱することで、これを 1 往復に圧縮しています。なお ClientHello は鍵共有前のメッセージなので暗号化できず、SNI のホスト名は経路上から見えます — 第 1 章の「HTTPS でもドメイン名は見える」の正体です。
帰り: ServerHello から Finished まで — 一気に畳みかける
サーバは選んだ方式と自分の公開値(key_share)を ServerHello で返します。この瞬間、両者は共有秘密を計算できます。TLS 1.3 はすぐに鍵を導出し、以降のメッセージをすべて暗号化した上で、同じ便にまとめて送ります。
クライアントは検証を終えると自分の Finished を返し、同じ便でアプリケーションデータ(HTTP リクエスト)を送り始められます。ClientHello からここまで 1 往復 — これが「1-RTT ハンドシェイク」です。
小問 5-1 — 1 往復の中身を追う
ハンドシェイクの各メッセージが「どの章の道具か」を意識しながら答えてください。
Q1. TLS 1.3 の ClientHello には、対応バージョンや暗号方式の候補と一緒に、DH の公開値(key_share)が最初から同梱されています。この設計の狙いはどれですか。
TLS 1.2 では方式の合意と鍵交換が別の往復に分かれ、ハンドシェイクに 2 往復かかっていました。TLS 1.3 はクライアントが「おそらくこの方式で通じるだろう」と見込んで公開値を初手で同梱し、サーバが自分の公開値を返した時点で鍵合意が完了します。設計の動機は暗号化ではなく往復の削減です。
Q2. クライアントとサーバの間の往復時間(RTT)が 100 ミリ秒とします。TCP 接続の確立後、ClientHello を送ってからクライアントが暗号化されたアプリケーションデータ(HTTP リクエストなど)を送り始められるまで、最短で何ミリ秒かかりますか。
ClientHello(行き)→ ServerHello〜Finished(帰り)で 1 往復 = 100 ミリ秒。クライアントは自分の Finished と一緒にアプリケーションデータを送り始められます。TLS 1.2 は 2 往復(200 ミリ秒)必要でした。この差が「TLS 1.3 = 1-RTT ハンドシェイク」の意味です。
Q3. サーバが送る CertificateVerify の役割はどれですか。
第 4 章で見たとおり、証明書は公開情報なのでコピーできます。そこでサーバは「ここまでのハンドシェイク全体」というその場でしか作れない内容に秘密鍵で署名します。過去の署名を再利用(リプレイ)することもできません。これが秘密鍵の所持証明で、なりすましを最終的に断つ一手です。
ハンドシェイクの後 — 共有秘密がそのまま鍵になるわけではない
細部ですが、実務の会話で効く点を 2 つ補っておきます。
鍵は用途別に導出される。第 3 章の共有秘密をそのまま暗号鍵に使うのではなく、鍵導出関数(HKDF)に通して「クライアント→サーバ用」「サーバ→クライアント用」など方向別・用途別の鍵を複数作ります。1 つの秘密から役割の違う鍵を派生させる — どれかが解析されても他に波及しにくくする設計です。
暗号化と改ざん検知は一体で行われる。本文の保護には認証付き暗号(AEAD。AES-GCM など)が使われ、暗号化と改ざん検知(第 2 章の MAC の役割)を 1 つの演算でまとめて行います。「暗号化はされているが改ざん検知を忘れた」という組み合わせ事故が起きない構造です。
また、一度接続したサーバとは、発行されたチケット(PSK)を使って次回のハンドシェイクを軽くするセッション再開もあります。さらに再開時には最初の便にデータを載せる 0-RTT という高速化もありますが、この便だけはリプレイ攻撃(攻撃者が同じデータをもう一度送りつける)を検知できないため、GET のような何度実行されても害のない操作に限る、という注意付きです。
小問 5-2 — 設計の意図を読む
「なぜそうなっているか」を答える問題です。第 1 章の伏線もここで回収します。
Q4. TLS 1.3 では、サーバが送る証明書(Certificate)はどんな状態で経路を流れますか。
ClientHello と ServerHello で公開値の交換が済んだ時点で、両者は共有秘密を計算できます。TLS 1.3 はここからすぐ鍵を導出し、証明書を含む以降のハンドシェイクメッセージを暗号化します(TLS 1.2 では証明書は平文で流れていました)。鍵合意を初手に置いた恩恵の 1 つです。
Q5. ハンドシェイクの最後に両者が交換する Finished メッセージの役割はどれですか。
Finished は、ハンドシェイクの全メッセージの記録から鍵付きで計算した検証値の突き合わせです。もし経路上の攻撃者が「候補から弱い暗号方式を削る」ような細工をしていれば、両者の記録が食い違い、ここで発覚します。会話の最後に議事録の指紋を照合するイメージです。名前に反して「終了宣言」ではありません。
Q6. 第 1 章で「HTTPS でも接続先のドメイン名は経路上から見える」と学びました。その理由として正しいものはどれですか。
1 つの IP アドレスで複数のドメインを運用するサーバは、どの証明書を出すべきかを最初に知る必要があります。そのため接続先ホスト名(SNI)は ClientHello に載りますが、ClientHello は鍵合意の「前」のメッセージなので暗号化のしようがありません。第 1 章の観察の正体はこれです(これを暗号化する ECH という拡張の普及も進みつつあります)。
この章で持ち帰ること
- TLS 1.3 はClientHello に公開値を同梱して 1 往復でハンドシェイクを終える。証明書以降はすでに暗号化されている
- CertificateVerify = その場の内容への署名による秘密鍵の所持証明。Finished = ハンドシェイク全体の改ざん検知
- 鍵は HKDF で用途別に導出し、本文はAEAD で暗号化と改ざん検知を一体で行う
- SNI は鍵共有前に流れるので見える。0-RTT は速いがリプレイに注意
仕組みの学習はここまでで一巡です。次章では openssl とブラウザを使って、この章まで頭で追ってきたものを実物で観察します。