前回⑧では、OpenSSLで実際の接続を見ました。ブラウザー側のClientHelloにサーバーがServerHelloで返答し、その後に証明書や署名、Finishedという確認のメッセージが続きました。今回は、その順序の意味を掘り下げます。共有秘密から用途と方向ごとの鍵を作る理由と、証明書・署名で接続相手を確かめる手順を追います。

暗号化と認証

 暗号化と認証⑨

 共有秘密から鍵を作り、証明書と署名で相手を確かめる

前回⑧では、OpenSSLで実際の接続を見ました。ブラウザー側のClientHelloにサーバーがServerHelloで返答し、その後に証明書や署名、Finishedという確認のメッセージが続きました。

今回は、その順序の意味を掘り下げます。共有秘密から用途と方向ごとの鍵を作る理由と、証明書・署名で接続相手を確かめる手順を追います。

⑦で作った共有秘密は、用途ごとの鍵を作る材料でした。その材料から通信を守る鍵を作ることと、相手を信用することが、どう一つの接続に結び付くのかを、古典的なRSA鍵輸送とも比べながら見ていきます。

一つの秘密から、なぜ何種類もの鍵を作るのか

一つの共有秘密からHKDFでハンドシェイク用・アプリケーション通信用・Finished確認用の鍵を方向別に導出する図
共有秘密から、通信の段階・用途・送信方向ごとに必要な鍵を導出する。

同じ秘密ができたなら、その値を一つの共通鍵として使えばよさそうにも見えます。

でもTLSでは、共有秘密を材料にして、仕事ごとの鍵を作ります。⑦の図で見たように、用途別・方向別の作り分けはSSL 3.0にもありました。ここでは、TLS 1.3で何をさらに分けたのかを見ます。

TLS 1.3ではHKDFを使います。秘密の材料、ここまでのやり取り、どの用途の値を作るかを区別する情報などを入れ、決められた手順で導出します。[1][2]

双方が同じ材料と同じ手順で計算するので、出来上がった鍵を改めて送り合う必要はありません。

接続準備を守る鍵と、HTTPを守る鍵

まず、通信の段階を分けます。

用途何を守るか今回の例
ハンドシェイク用相手や接続条件を確認するためのメッセージ証明書、CertificateVerify、Finishedなど
アプリケーション通信用接続後にやり取りするデータHTTPリクエスト、Webページの内容など

どちらも、今回取り上げる構成ではAES-GCMで暗号化し、改ざんを検出します。使う暗号方式は同じでも、鍵は別です。

接続準備に使った鍵を、その後のデータ通信でも使い続ける構成にはしていません。準備から実際の通信へ進むところで、使う鍵も切り替わります。

アプリケーション通信用の秘密から、接続中に次の世代の鍵を作ることもできます。TLSのKeyUpdateです。これは、新しい用途が増えるというより、同じ用途の鍵を更新する仕組みです。[1]

行きと帰りでも、鍵を分ける

さらに、それぞれを送信方向で分けます。

送る方向送信側が使う鍵受信側が使う鍵
ブラウザー → サーバーブラウザーの送信用の鍵それと同じ、サーバーの受信用の鍵
サーバー → ブラウザーサーバーの送信用の鍵それと同じ、ブラウザーの受信用の鍵

一行の中では同じ鍵を共有しています。しかし、行きの鍵と帰りの鍵は違います。

ハンドシェイク用に行きと帰り、アプリケーション通信用にも行きと帰り。まず、この四つの暗号化用の鍵があると考えると整理できます。

各方向には、暗号化に使うIVの材料やメッセージの通し番号もあります。AES-GCMでは、同じ鍵で同じnonceを繰り返さないことが必要です。方向ごとに鍵とIVを分け、各方向の番号を使ってnonceを作ることで、行きと帰りを独立して管理できます。[1]

Finishedは、暗号化とは別の確認をする

最後に、Finishedを計算するための鍵も作ります。これも、ブラウザー用とサーバー用で分かれています。

この鍵は、Webページを暗号化するための鍵ではありません。HMACという計算で、ここまでのハンドシェイクを照合する値を作ります。

AES-GCMの改ざん検出は、その鍵で保護したメッセージを確かめます。一方、Finishedでは、まだ暗号化を始めていなかったClientHelloやServerHelloも含め、接続開始からのやり取りを照合します。

「途中からの通信を守る」と、「最初からの手続き全体が食い違っていないか確認する」。この二つも、担当する仕事が違います。[1]

Finishedを計算する鍵は、証明書に対応するRSA秘密鍵とも別です。CertificateVerifyの署名はRSA秘密鍵で作り、Finishedの確認値は、鍵交換から導出した確認用の鍵で作ります。

作り分けることで、仕事同士を切り離せる

TLS 1.3では、AEADで通信を守る鍵と、Finishedを計算する鍵を分け、接続準備と実際の通信も分けます。用途や方向の違う鍵を、そのまま別の場所で使い回さない設計にできます。

また、適切に導出したある用途の完成した鍵だけが漏れても、それから元の秘密や他の用途の鍵を求めるのは困難です。元の秘密や、それぞれの導出元まで漏れれば、そこから作れる鍵も影響を受けるので、何が漏れたかは区別する必要があります。

TLSには、次回の接続を再開するためのPSKを作る秘密や、別の用途へ鍵の材料を提供するexporter用の秘密もあります。0-RTTを使う場合には、初期データ用の鍵もあります。今回は初回の証明書認証を追っているので、まずは接続準備、実際のデータ、最後の照合という三つの用途と、それぞれの方向を押さえます。[1]

共有秘密ができた段階では、通信に使う鍵はまだ完成していません。ここで揃ったのは、用途ごとの鍵を作るための材料です。

証明書を見ていないのに、もう暗号化している?

ここで、前回⑧で見たTLS 1.3のメッセージの順序へ戻ります。

ClientHelloとServerHelloで鍵交換が進み、その結果から、後続のハンドシェイクを暗号化する鍵が作れます。

TLS 1.3では、ServerHelloの後に送られるEncryptedExtensions、Certificate、CertificateVerify、Finishedなどは、その鍵で保護されます。[1]

つまり、証明書の検証が終わる前に、暗号化は始まっています。

最初は、順序が逆に見えます。

「相手を信用できると確認してから、暗号化するのでは?」

でも、これは①で考えた話そのものです。

暗号化できることと、その相手が本物だと確認できることは、別です。

この段階では、鍵交換で秘密を作り、メッセージを保護できるようになった。続いて、その鍵交換に参加している相手を、証明書と署名で確かめていきます。

TLSは、その二つを一つのハンドシェイクの中で結び付けています。

Certificateで、⑤の証明書チェーンが届く

ここで、ようやく⑤の証明書が登場します。

Certificateメッセージでは、サーバー証明書や、チェーンの構築に必要な中間証明書などが送られます。

クライアントは、それらを使って、手元の信頼の起点までつながるかを調べます。

それに加えて、有効期間、用途、接続先のホスト名との対応なども確認します。失効情報の扱いなどは、クライアントの実装や検証方針にも関係します。[3][4]

⑤で書いた、中間証明書不足でApacheへの接続がエラーになった話も、この確認につながっています。

相手が証明書を出してきても、信頼しているルートまでたどれなければ、そのまま信用することはできません。

前回⑧の実験は、中間CAを挟まず、実験用CAがサーバー証明書へ直接署名する構成です。クライアントが-CAfile lab-ca.crtでそのCAを信用することで、検証が通ります。

また、CAがアクセスのたびに、この接続へ立ち会って署名しているわけではありません。

証明書に付いているCAの署名は、発行時のものです。

では、目の前のサーバー自身は、今回の接続で何を証明するのでしょう。

証明書のコピーだけで「本人」になれる?

CAが証明書の内容に署名する役割と、サーバーが証明書を含む今回の接続のやり取りに署名する役割を比較した図
CAの署名で証明書の内容を確かめ、CertificateVerifyで秘密鍵の所持と今回の接続への参加を確かめる。

証明書は、接続してきた相手へ送るものです。秘密鍵のように、誰にも見せずに保管するものではありません。

だったら、誰かが本物の証明書をコピーして出してきたら、それだけで本人になれてしまうのでしょうか。

ここで、図にある二つの署名が働きます。

CAの署名は、証明書の内容を保証する

図の左側は、証明書を発行したCAの署名です。

CAは、サイト名や公開鍵など、証明書の内容に署名します。クライアントはその署名を検証し、⑤で追った信頼の経路も確かめます。

ただし、証明書をコピーしても、そこに付いたCAの署名はそのままです。本物の証明書を提示できることだけでは、提示した相手が対応する秘密鍵を持っているとは分かりません。

サーバーの署名は、今回の接続への参加を証明する

そこで登場するのが、図の右側のCertificateVerifyです。

サーバーは、証明書の公開鍵と対になる秘密鍵を使い、今回の接続でここまでやり取りした内容に結び付くデータへ署名します。クライアントは、証明書に入っている公開鍵で、その署名を検証します。[1]

ここまでのやり取りには、CAが署名した証明書も含まれます。ただし、証明書だけに署名を重ねるのではありません。ClientHelloやServerHelloも含め、今回の接続のやり取りに結び付けて署名します。

そのため、鍵交換で渡した公開情報も、相手の確認と結び付きます。

証明書をコピーしただけでは、対応する秘密鍵は手に入りません。その秘密鍵がなければ、今回の接続について有効なCertificateVerifyの署名を作れません。別の接続で使われた署名をコピーしても、今回のやり取りとは一致しません。[1]

CAの署名で、証明書の内容を確かめる。サーバーの署名で、その公開鍵に対応する秘密鍵を持つ相手が、今回の接続に参加していると確かめる。

⑤で見た「紹介状」と、今回の「本人確認」が、ここでつながります。

Finishedで、ここまでの話を照合する

CertificateVerifyの後には、サーバーのFinishedが来ます。

これは、単なる「終わりました」という通知ではありません。

先ほど作り分けた確認用の鍵で、ここまでのやり取りを照合する段階です。秘密を作れたことに加え、双方が食い違う手続きを見ていないかを確かめます。

送り手は、導出した秘密値から作る鍵と、ここまでのハンドシェイクの内容を使い、HMACによる照合用の値を計算します。受け手も対応する計算を行い、その値が正しいか確かめます。[1]

クライアントも、サーバー側の確認を済ませてから、自分のFinishedを送ります。

鍵交換の公開情報を途中で変えたり、方式の選択をすり替えたりして、双方が違う手続きを見ているなら、この整合性を保てません。

ここまで来ると、各メッセージが担当する役割も見えてきます。

段階主な役割
ClientHello / ServerHello方式を決め、鍵交換に必要な情報を交換する
EncryptedExtensionsALPNなど、追加の接続条件を伝える
Certificate証明書を送り、信頼の経路と接続先との対応を確認できるようにする
CertificateVerify証明書の秘密鍵を持つ相手が、今回の手続きに参加していることを示す
Finishedここまでのやり取りと、導出した鍵の整合性を確認する

なお、ここで扱う構成では、サーバーがクライアント証明書を要求しません。クライアントがFinishedを送ったことだけで、接続した人の身元まで証明されたわけではありません。

「共通鍵の材料」を、仕様で確かめる

TLS 1.3で公開鍵がどう働くかを見たので、⑥のSSL通信の説明へ戻ります。

⑥で追った流れを短くすると、「クライアントが共通鍵の材料を作る。それをサーバーの公開鍵で暗号化して送り、サーバーが秘密鍵で復号する」という説明になります。

この説明にも、元になった方式があります。RSA鍵輸送です。

SSL 3.0の仕様には、クライアントが48バイトのpre_master_secretを用意し、サーバーのRSA公開鍵で暗号化して送る手順が書かれています。サーバーは、対応する秘密鍵でそれを取り出します。[5]

⑥でも、暗号化して渡すものを「共通鍵の材料」と呼んでいました。

暗号化して送るのは、最終的な通信の共通鍵そのものではなく、その鍵を作るための秘密の材料です。

この材料をpremaster secretと呼びます。TLS 1.2のRSA方式では、48バイトの内訳は、バージョン情報2バイトと、安全に生成した乱数46バイトです。[6]

TLS 1.2のRSA鍵輸送で整理すると、次のようになります。[6]

段階クライアントサーバー
証明書の確認サーバー証明書を検証し、RSA公開鍵を得る証明書を送る
秘密の材料を用意premaster secretを作るまだその値を知らない
材料を渡すRSA公開鍵で暗号化し、ClientKeyExchangeで送るRSA秘密鍵で復号する
通信用の鍵を作るpremaster secretなどから鍵を導出する同じ材料から対応する鍵を導出する

ClientHelloとServerHelloで交換した乱数なども鍵導出に使います。premaster secretからmaster secretを作り、そこから送信方向ごとの通信鍵などを作る、という流れです。

「公開鍵で共通鍵を暗号化して渡す」は、この一連の処理を短くまとめた説明だと分かります。

ただし、RSA鍵輸送はSSLだけのものではありません。SSLからTLSへ名前が変わった後も、TLS 1.0、1.1、1.2の仕様に残っていました。

一方、Diffie–Hellman方式も、すでにSSL 3.0の仕様にあります。今回のように双方で秘密を計算する考え方が、TLS 1.3で突然生まれたわけでもありません。[5][7]

プロトコルの名前に加え、その接続でどの鍵交換方式を選んだかを見る必要があります。

秘密鍵が後から漏れたら、昔の通信も読める?

RSA鍵輸送では、通信の途中に、RSAで暗号化されたpremaster secretが流れます。

その時点では、盗み見した人もサーバーの秘密鍵を知らないので、材料を取り出せません。

では、その人が暗号化された通信を全部保存していたら、どうでしょう。

数か月後、数年後に、サーバーのRSA秘密鍵だけが漏れたら。

保存していたClientKeyExchangeのRSA暗号文を、その秘密鍵で復号できてしまいます。ClientHelloとServerHelloの乱数なども記録に残っていれば、過去の通信に使った鍵を導出するための材料が揃います。

通信していた当時は秘密鍵が守られていても、後からその鍵を失うことで、保存済みの通信まで影響を受ける。

RSA鍵輸送には、この性質があります。[6][8]

前回⑧で触れた前方秘匿性は、後から長期の秘密鍵が漏れても、過去の通信の秘密を守れるという性質です。ここで比べているのは、その性質があるかどうかです。

一時的なECDHE鍵交換なら、証明書の長期RSA秘密鍵でpremaster secretを暗号化して送っているわけではありません。各接続の一時秘密鍵が安全に破棄されていれば、後から証明書の秘密鍵だけが漏れても、保存していた公開情報から過去の共有秘密を再計算できる構成にはなっていません。

後から証明書のRSA秘密鍵だけが漏れた場合RSA鍵輸送一時的なECDHE鍵交換
保存済みの鍵交換メッセージから秘密の材料を取り出せるかRSA暗号文を復号して取り出せる証明書の秘密鍵から一時秘密鍵は得られない
過去の通信を、その長期鍵の漏えいから守る性質前方秘匿性がない一時秘密鍵を安全に破棄していれば、前方秘匿性を持つ

これで、一時的な鍵を別に用意する理由が見えてきます。

また、古いRSA鍵輸送は、RSA暗号化のパディングにPKCS #1 v1.5を使います。②のC#コードで使ったRSA-OAEPと、同じ形式ではありません。[6]

このパディングを正しく処理できたかどうかが、エラーや応答時間から漏れると、暗号文の内容を絞り込む攻撃につながります。Bleichenbacher型の攻撃として知られ、仕様には対策も書かれています。[6][8]

前方秘匿性がないことに加え、そうした対策を実装し続ける難しさも、RSA鍵輸送を外す理由に含まれています。

RSAを使う場所が変わってきた背景には、暗号の計算だけでなく、鍵が漏れた後の影響や、プロトコルを安全に実装する難しさもありました。

RSA証明書を残したまま、鍵交換を変える

では、RSA鍵輸送をやめるために、RSA証明書もすべて交換する必要があったのでしょうか。

そこで登場するのが、ECDHE_RSAです。

ECDHEで、その接続の共有秘密を作る。そして、その鍵交換に参加しているサーバーを、RSA署名で確かめる。

RSA公開鍵を使う証明書を残しながら、通信に使う秘密を作る役割を、別の一時鍵へ分けられます。2006年のRFC 4492にも、既存のRSA証明書を使ってECDHE_RSAを利用できることが説明されています。[9]

TLS 1.2の暗号スイート名を比べると、この違いが出ています。

暗号スイート名鍵交換・認証通信の暗号
TLS_RSA_WITH_AES_256_GCM_SHA384RSA鍵輸送AES-256-GCM
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384ECDHEの鍵交換とRSA署名AES-256-GCM

AES-GCMを使っているという部分は同じです。でも、最初にその通信鍵の材料をどう作るかが違います。[10][11]

この二つ目の名前に入っているRSAは、共通鍵の材料をRSAで暗号化して運ぶ、という意味ではありません。鍵交換を認証する署名の役割です。

そして、ここで取り上げるTLS 1.3では、その分離が暗号スイート名にも反映されています。

TLS_AES_256_GCM_SHA384という名前には、RSAもX25519も入っていません。署名方式と鍵交換方式を、別の項目として選ぶからです。

CertificateVerifyがないのに、どうやって本人を確かめる?

TLS 1.2のRSA鍵輸送では、サーバーがCertificateVerifyを送っていません。

だったら、コピーした証明書を出すだけで、本人になれてしまうのでしょうか。

ここでは、サーバーが秘密鍵でpremaster secretを復号できることが効きます。その材料から正しい鍵を作り、正しいFinishedを返せることによって、対応する秘密鍵を持っていることを示します。[6]

ECDHE_RSAの場合は、ServerKeyExchangeにRSA署名が付いています。この署名は、鍵交換のパラメーターと、ClientHello・ServerHelloの乱数に結び付いています。[9]

一方、TLS 1.3では、サーバーのCertificateVerifyで、それまでのハンドシェイクに結び付いた署名を作ります。

比較する方式サーバーの秘密鍵の所持をどう確かめるか
TLS 1.2のRSA鍵輸送秘密の材料を復号した相手が、正しいFinishedを作れること
TLS 1.2のECDHE_RSAServerKeyExchangeの署名と、後続のFinished
TLS 1.3の証明書認証CertificateVerifyの署名と、後続のFinished

証明書を信用することに加えて、いま接続している相手が、その証明書に対応する秘密鍵を持っていると確かめる。

その目的はつながっていますが、具体的な手順は同じではありません。

TLS 1.2にもCertificateVerifyという名前はあります。ただし、そこでは署名できるクライアント証明書を使う場合のメッセージです。前回⑧の、クライアント証明書を使わない実験には出てきません。[6]

メッセージ名だけを別のバージョンへ当てはめず、誰が、何のために送るのかまで確認する必要があります。

TLS 1.3は、鍵交換の位置まで変えた

ここで、TLS 1.3の接続手順を、TLS 1.2と比べてみます。

「証明書を見る前に、もう暗号化しているのはなぜ?」

TLS 1.2のECDHEでは、サーバー証明書に続いてServerKeyExchangeが届き、その後にクライアントもClientKeyExchangeを送っていました。

TLS 1.3では、鍵交換の公開情報をClientHelloとServerHelloへ持ってきています。早い段階でハンドシェイク用の鍵を作れるので、続く証明書や署名も暗号化して運べます。

RSA鍵輸送を外しただけでなく、相手の認証と鍵交換を分け、それを結び付ける手順も組み替えたわけです。[1][12]

見る項目TLS 1.2の初回接続TLS 1.3の初回接続
サーバー証明書この時点ではTLSレコードで暗号化されていないServerHello後の鍵で暗号化して届く
鍵交換ClientKeyExchangeなどを使うHelloのkey_shareを使う
Finishedの順序クライアントから、次にサーバーサーバーから、次にクライアント
暗号スイート名鍵交換・認証の方式も含むレコード保護の暗号と、鍵導出などのハッシュを表す

この違いを踏まえると、教科書のRSA鍵輸送の説明と、TLS 1.3の接続手順を、同じ手順として無理につなげずに読めます。

さらに、2026年7月にはRFC 10015が公開され、TLS 1.2でもRSA鍵輸送を提示・選択しないことが要求されました。署名にRSAを使うECDHE_RSAは、このRSA鍵輸送の廃止とは区別されます。[8]

同じ月のRFC 9846では、TLS 1.3の仕様も改訂されています。バージョンは1.3のまま、既存の接続との互換性を保ちながら、接続間でのKeyShareの再利用禁止などを明確にしています。[12]

「TLS 1.3」というバージョン名が同じでも、仕様文書は更新されている。その点まで見ると、1990年代のSSLから、いまのTLSへ続く変化が見えてきます。

これで、証明書の公開鍵の仕事と、通信の鍵を作る仕事が分かれたことも、その間を署名で結び付ける意味も、つながってきました。

次回

次回は「暗号化と認証⑩ ― 証明書の検証と、C#・IPsecから見る接続」を取り上げます。

証明書の名前や信頼の起点が違うときのエラーを確かめ、C#・IPsecからも接続の境目を見ます。

参考資料

  1. RFC 8446 — TLS 1.3:§2、§4.4、§6.1、§7。ハンドシェイク、CertificateVerify、Finished、鍵導出、接続の終了。
  2. RFC 5869 — HKDF:鍵導出の基本。
  3. RFC 5280 — X.509 PKI Certificate and CRL Profile:証明書とパス検証。
  4. RFC 9525 — Service Identity in TLS:接続先の名前と証明書の対応。
  5. RFC 6101 — SSL Version 3.0:1996年の仕様の記録。RSA鍵輸送とDiffie–Hellman。
  6. RFC 5246 — TLS 1.2:旧来のRSA鍵交換と、そのほかの方式。
  7. RFC 2246 — TLS Version 1.0:SSL 3.0との関係と、RSA鍵輸送。
  8. RFC 10015 — Deprecating Obsolete Key Exchange Methods in TLS 1.2 and DTLS 1.2:2026年7月のRSA鍵輸送廃止と、その理由。
  9. RFC 4492 — ECC Cipher Suites for TLS:ECDHE_RSAと、既存RSA証明書による鍵交換の認証。
  10. RFC 5288 — AES-GCM Cipher Suites for TLS:RSA鍵輸送を含むAES-GCM暗号スイートの歴史。
  11. RFC 5289 — TLS ECC with SHA-256/384 and AES-GCM:ECDHE_RSAなどでのAES-GCM。
  12. RFC 9846 — TLS Version 1.3:2026年7月の改訂。RFC 8446との関係、KeyShare再利用禁止など。

返信を残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA