TCP 3 Way Handshake and TLS Full Handshake

最近プロフェッショナルSSL/TLSを少しずつ読んでいます。

一章と二章を読み終わったところで、二章の TLS フルハンドシェイクについて整理してみたくなったので整理します。

また、 TLS より下位レイヤーの TCP 3 ウェイハンドシェイクについても併せて整理してみます。

TLS フルハンドシェイクまでのおおまかな流れ

  1. クライアントとサーバが TCP 3 ウェイハンドシェイクを行い TCP コネクションを確立する
    • TCP のペイロード(送信先情報など通信に必要なヘッダを除いたパケット部分)にデータ(より上位のプロトコル等)を乗せて通信できるようになる
  2. TCP コネクション上で TLS フルハンドシェイクを行い TLS コネクションを確立する

TCP 3 ウェイハンドシェイク

TCP は OSI 参照モデルで言うところのトランスポート層(第 4 層)に当たる。

3 ウェイの名前の通り、 3 方向のやり取りを行っている。

3ウェイ・ハンドシェイク - Wikipedia

  1. クライアントからサーバに SYN ( Synchronize ) ビットを 1 に、 ACK ( ACKnowledgement ) ビットを 0 にセットした TCP セグメントが送信される(コネクション確立の要求)
    • SYN ビットは ACK ビットとの組み合わせでコネクションの要求なのかコネクションの受け入れなのかを示すのに利用される
    • ACK ビットは確認通知番号(それまでに受信したデータの数を示す)が有効であるかどうかを示し、 0 の場合は確認通知番号は無視される(これまでの通信がない = 新規接続)
    • セグメントとは TCP に使われる PDU (コンピュータ間の通信に使用されるデータを送る単位) の呼び名
      • ちなみにパケットはネットワーク層の通信で使われる PDU の呼び名
      • ただしパケットは広義にあるデータのひとかたまりという意味もあるので TCP パケットと呼ばれることもある
  2. 1 の TCP セグメントがサーバに到達したら、サーバは SYN ビットを 1 に、 ACK ビットを 1 にセットした TCP セグメントを返す(コネクション受け入れ)
    • コネクション要求への返答と、クライアントに対するコネクション確立の要求を両方行っていることになる
    • このように返信と要求を同時に行う手法はピギーバックと呼ばれる(やりとりの手数を減らすことによる帯域の有効活用と処理の負荷軽減)
  3. クライアントがサーバに ACK を 1 にセットした TCP セグメントを送る(コネクションの確立)

SYN やら ACK やら確認通知番号やらは TCP セグメント・ヘッダ内に収められている。

Transmission Control Protocol - Wikipedia

TLS フルハンドシェイク

TLS は OSI 参照モデルで言うところのプレゼンテーション層(第 6 層)に当たる。

TLS はトランスポート層での通信のためのセキュリティを提供する。逆に言えばトランスポート層より下の層のセキュリティに関しては TLS は無力である(例えば TLS 通信していようが送信先の IP アドレスは観察できてしまう)。

クライアントがそれ以前にサーバとセッションを確立したことが無い場合、フルハンドシェイクによって TLS セッションを確立する。

TLS ハンドシェイクプロトコル

  1. クライアントがサーバに希望する暗号スイートや鍵交換の方法などのパラメータ群をサーバに送る( ClientHello )
    • 暗号スイートとは複数の暗号技術の組み合わせ
    • なぜ組み合わせるかというと、セキュリティの 3 つの問題(機密性、真正性、完全性)に適した暗号技術はそれぞれ異なるから
  2. サーバがパラメータを決定する( ServerHello )
  3. サーバが自身の証明書チェーンを送る( Certificate )
    • 必須ではない。サーバ認証が必要な場合のみ
  4. マスターシークレットの生成に必要な情報がある場合サーバからクライアントへ送る( ServerKeyExchange )
    • 必須ではない
    • マスターシークレットとは共通鍵の生成に使用するためのもの
  5. 交渉の自分の番が終わったことをサーバからクライアントへ通知する( ServerHelloDone )
  6. マスターシークレットの生成に必要な情報をクライアントから送る( ClientKeyExchange )
  7. クライアント側で暗号通信に切り替え、そのことをサーバに通知する( ChangeCipherSpec )
    • ChangeCipherSpec はハンドシェイクメッセージではなく、 TLS ハンドシェイクのサブプロトコルである ChangeCipherSpec のメッセージ
  8. クライアントから送信及び受信したハンドシェイクメッセージの MAC ( Message Authentication Code ) を送る( Finished )
    • MAC とはメッセージが改竄されていないことを保証する暗号学的な関数
  9. サーバ側で暗号通信に切り替え、そのことをクライアントに通知する( ChangeCipherSpec )
  10. サーバから送信及び受信したハンドシェイクメッセージの MAC を送る( Finished )

実際のパケットの中身を見てみる

Wireshark をインストールし、パケットの中身を覗いてみます。

brew cask install wireshark

クライアント( Mac )からサーバ( https://blog.lorentzca.me/ )へのパケットを見てみます。

上のパケットは以下のフィルタで絞ることが出来ます。

tcp.port == 443 and ip.dst == 188.166.206.17

TCP セグメントの中身です。コネクションの要求と、コネクション確立のパケットが送られていますね。コネクションの要求では ACK が 0 、コネクション確立時は ACK をセットしているので ACK の値が増えています。

そして TLS コネクションの確立が終わった後に、 TLS の ClientHello が行われています。暗号スイート( Cipher Suites )の一覧が送られているのも確認できます!

まとめ

TCP と TLS それぞれのハンドシェイクについて整理しました。

両方名前が似ているしハンドシェイクプロトコル持っているし、ややこしいと思っていたのですが、違いと役割を整理できて良かったです。

ハンドシェイクについては事ある毎に調べなおしては忘れているので、今回整理できてスッキリした気分です。調べていくとどんどんわからない単語が出てきますが、手元に TLS 本とネットワーク本があったので比較的スムーズに理解が進みました。

ヤックシェービングは通常は人間を疲弊させるものですが、あえて積極的にヤックシェービングしていく時間を持つことは継続していきたいですね…。

参考リンク・参考書籍

リンク

書籍