← 記事一覧

テクノロジー

SSL/TLS入門 | すぐ忘れるTLS・ハンドシェイクの流れ

はじめに

mTLS(Mutual TLS・相互TLS通信)について仕組みを調べていたのですが、そもそも基盤となるTLS通信の理解がおざなりだったので、改めて調べ直しました。今回は、現在の主流であるTLS1.3をベースにTLS通信の仕組みとハンドシェイクの流れを整理します。

TLS通信の仕組みと流れ

TLSは、公開鍵暗号方式と共通鍵暗号方式を組み合わせて実現されています。

クライアント(ブラウザやOSなど)がHTTPSで保護されたリソースを取得する際、まず最初にクライアントとサーバの間で安全な通信を確立するための挨拶を行います(ハンドシェイク)。

ハンドシェイクが確立すると、生成された共通鍵を用いてデータが暗号化され、安全な通信が行われます。

なぜ、公開鍵と共通鍵の両方を使うのか?

TLS通信では、共通鍵を用いて通信を暗号化します(共通鍵暗号方式)。

では、公開鍵・秘密鍵は何に使われるのでしょうか?

共通鍵による暗号化は処理が早いという利点がありますが、

どのように共通鍵をお互いに知らせるか?

という問題があります。

共通鍵をそのままクライアント側に送ってしまうと、盗まれてしまうと通信が復号されて覗かれてしまいます。

それを防ぐため、最初の共通鍵の受け渡し(*)の部分だけ「公開鍵暗号方式」を使って安全に共通鍵を共有します。

*実際には、共通鍵を生成する素材を公開鍵暗号方式で暗号化して共有する。共通鍵はサーバ側・クライアント側でその素材を使ってそれぞれで生成する。

ハンドシェイクの流れ(TLS1.3)

TLS 1.3 では、ClientHello と ServerHello で鍵共有を行い、サーバ証明書を含む以降のハンドシェイクを盗聴・改ざんから保護するための共通鍵を導出します。つまり、ServerHelloの直後から通信は暗号化されます。


ただしこの時点では、クライアントは接続相手が正規のサーバかをまだ確認できていません。後から届くサーバ証明書と CertificateVerify の署名を検証して、初めて正規のサーバだと判断します。

1. ClientHello

クライアントは接続を開始するため、次の情報をサーバへ送ります。

  • 対応する TLS バージョン
  • 利用可能な暗号スイート
  • 鍵共有に使う一時公開鍵(`key_share`)
  • client_random などの接続ごとに異なる値

client_random はクライアントが接続ごとに生成するランダムな値です。ServerHelloにも同様の server_random が含まれます。これらの値はハンドシェイク内容の一部として署名や Finished の検証対象になり、接続ごとの通信を結び付けます。

TLS 1.3 でハンドシェイク用鍵を導出する中心となるのは key_share による鍵共有です。client_random自体を通信の暗号鍵として使うわけではありません。

2. ServerHello とハンドシェイク用鍵の導出

サーバは使用する TLS バージョン・暗号スイート・鍵共有用の一時公開鍵を選び、ServerHelloで返します。

両者はそれぞれの一時鍵から共有秘密を計算し、そこからハンドシェイク用の共通鍵を導出します。

この時点で、後続のハンドシェイクメッセージは暗号化できますが、クライアントはまだサーバを認証していません。

3. この接続で使う鍵

TLS 1.3 では、鍵共有で得た値から用途ごとに異なる鍵を導出します。

種別

役割

一時鍵ペア(key_share)

鍵共有に使う。通信の暗号化には直接使わない。

共有秘密

クライアントとサーバが鍵共有で計算する、鍵を導出するための共通の材料。

ハンドシェイク用共通鍵

Certificate、CertificateVerify、Finishedなどを暗号化する。

アプリケーションデータ用共通鍵

ハンドシェイク完了後の HTTP などを暗号化する。

送信方向ごとに別の共通鍵が使われます。

また、証明書に対応する公開鍵・秘密鍵はサーバを認証するための鍵であり、通信を暗号化する上記の鍵とは別物です。

4. 暗号化されたサーバ認証

ServerHello の後、サーバは暗号化されたハンドシェイクメッセージを順に送ります。

  • EncryptedExtensions: ClientHello で要求された拡張機能に対するサーバの応答を、暗号化して通知する。たとえば使用するアプリケーションプロトコル(ALPN)などを含む
  • Certificate: サーバ証明書チェーンを送信 (サーバ証明書・中間証明書)
  • CertificateVerify: それまでのハンドシェイク内容に対する署名を送信し、証明書の秘密鍵を保持していることを証明
  • Finished: ここまでのハンドシェイク内容が改ざんされていないことを確認するメッセージ

クライアントは証明書チェーンの信頼性・有効期限・接続先名を確認し、さらに証明書内の公開鍵で CertificateVerify の署名を検証します。ここで初めて、接続相手が正しいサーバであることを確認できます。

5. Client Finished とアプリケーションデータ

検証に成功したクライアントは Finished を返します。これにより双方がハンドシェイク内容を確認し、TLS ハンドシェイクが完了します。

以降は、ハンドシェイクから導出したアプリケーションデータ用の鍵で、HTTP などの通信を暗号化します。


* クライアント証明書による認証(mTLS)を使う場合は、サーバの CertificateRequest に続けて、クライアントも Certificate、CertificateVerify、Finishedを送ります。

以上がTLS 1.3におけるハンドシェイクの流れです。

おわりに

TLS 1.3の大きな特徴は、TLS 1.2以前と比べて通信速度(パフォーマンス)が改善している点です。

TLS 1.2では暗号化通信の確立までに「2往復(2-RTT)」のやり取りが必要でしたが、TLS 1.3では最初のClientHelloの時点で共通鍵の素材も一緒に送信することで、通信確立を「1往復(1-RTT)」で済ませることができるようになりました。

今回、基盤となるTLS 1.3の仕組みと鍵交換の流れを整理できたことで、認証の解像度が大きく上がりました。この前提知識をもとに、次はいよいよ当初の目的であった「mTLS(相互TLS通信)」の仕組みについてまとめていきたいと思います。