Wireshark で TLS ハンドシェイクの流れを見てみる

現在ではごくごく当たり前に Web サイトが https でアクセスされていると思いますが、クライアントとサーバー間でどのようなやり取りが行われいるかについて、気になったことはないでしょうか？

かく言う私も、「あーこのサーバーは VeriSign の証明書を使ってるだなー」くらいにしか気にしていなくて、
クライアントが Web アプリケーションを使えるようになる前に、どのようなやり取りがされているかなんて意識したことがありませんでした。

今回は、名著「暗号技術入門 秘密の国のアリス」に記載されている、 TLS ハンドシェイクの流れを実際に、パケットキャプチャ ツールの王道「Wireshark」を使って実際のデータを見てみたいと思います。

#TLS プロトコルとは
TLS プロトコルは、「TLSレコード プロトコル」「TLSハンドシェイク プロトコル」という 2 つのプロトコルによって構成されており、下層の TLS レコード プロトコルが暗号化の処理を行い、上層の TLS ハンドシェイク プロトコルが暗号化以外のさまざな処理を行います。

TLS ハンドシェイク プロトコルは、さらに 4 つのサブプロトコルに分かれており、その中の 1 つが「ハンドシェイク プロトコル」です。

#ハンドシェイク プロトコルとは
ハンドシェイク プロトコルとは、 共有鍵を生成し、証明書を交換するためのプロトコルです。共有鍵を生成するのは、暗号通信を行うためであり、証明書を交換するのは、互いに相手を認証するためです。

ハンドシェイクという言葉は 「握手」という意味ですね、ネットワークを少しでも習ったことがある人は TCP/IP の 3 way ハンドシェイクという言葉でお馴染みだと思います。

TLS の世界では、暗号通信に先立って、サーバーとクライアントが必要な情報交換をするということを「握手」と表現しています。
最初のハンドシェイク プロトコルでのやりとりは、暗号化されていない状況で行われます。

つまり、このプロトコルの途中で流れるすべてのデータは、盗み出される可能性があります。
ですので、公開鍵暗号もしくは Diffie-Hellman 交換鍵をうまく使う必要があります。

#ハンドシェイクの流れ
まず、 Wireshark でパケットを見る前に、いかに全体のハンドシェイクの流れを記載します。
「暗号技術入門 秘密の国のアリス」に非常に分かりやすく書かれているので、その流れを引用します。

#####(1) ClientHello (クライアント→サーバー)

クライアントがサーバーへ「ClientHello」というメッセージを送ります。

クライアント「こんにちは。私が理解できる暗号スイートは、 RSA と 3DES を使ったものか、 DSS と AES を使ったものです。何を使って通信しましょうか。」

このとき、以下の情報がサーバー送られます。

・使用できるバージョン情報
・現在時刻
・クライアント ランダム
・セッション ID
・使用できる暗号スイートの一覧
・使用できる圧縮方法の一覧

#####(2) ServerHello (クライアント←サーバー)

クライアントからの ClientHello に対して、サーバーは ServerHello というメッセージを返します。

サーバー「こんにちは。それでは RSA と 3DES を使ったもので通信しましょう。」

サーバーは ServerHello メッセージにあわせて、以下の情報を送ります。

・使用するバージョン情報
・現在時刻
・サーバー ランダム
・セッション ID
・使用する暗号スイート
・使用する圧縮方法

サーバーは、クライアントが ClientHello で送ってきた情報を基にして、「使用するバージョン番号」「使用する暗号スイート」「使用する圧縮方法」を決定します。

#####(3) Certificate (クライアント←サーバー)

サーバーが Certificate メッセージを送ります。

サーバー「はい、これが私の証明書です」

Certificate メッセージにより、クライアントに以下の情報が送られます。

・証明書リスト

証明書リストは、 X.509v3 の証明書の列で、サーバー(送り主)の証明書から始まり、その証明書に署名している認証局の証明書が順番に続きます。

※実はここが今回一番お伝えしたい内容になります。ブラウザーの「鍵マーク」をクリックすると、証明書の情報が表示できますよね、もちろんブラウザーで見られるに越したことはないのですが、ちゃんとパケットの中にも証明書情報が TLS ハンドシェイクの中で流れているんですよ、ということを後程 Wireshark でお見せしたいと思います。


クライアントは、サーバーから送られてきた証明書を検証します。

#####(4) ServerKeyExchange (クライアント←サーバー)

サーバーが ServerKeyExchange メッセージを送ります。

サーバー「この情報を使って鍵交換をしましょう」

ServerKeyExchange メッセージは、 Certificate メッセージだけでは情報が不足であるとき、クライアントに対して必要な情報を送るためのものです。どのような情報を送るかは、暗号スイートの内容によって異なります。

#####(5) CertificateRequest (クライアント←サーバー)

サーバーが CertificateRequest メッセージを送ります。

サーバー「ところで、あなたの証明書も見せてほしいです。」

CertificateRequest メッセージは、サーバーがクライアントに対して証明書を要求するためのものです。このメッセージは**クライアント認証**のためにあります、このメッセージで、クライアントに以下の情報が送られます。

・サーバーが理解できる証明書のタイプ一覧
・サーバーが理解できる認証局の名前一覧

クライアント認証を用いない場合には、 CertificateRequest メッセージは送られません。

#####(6) ServerHelloDone (クライアント←サーバー)

サーバーが ServerHelloDone メッセージを送ります。

サーバー「以上がごあいさつです」

このメッセージは、 ServerHello メッセージから始まる一連のメッセージの終わりを示します。

#####(7) Certificate (クライアント→サーバー)

クライアントが Certificate メッセージを送ります。

クライアント「私の証明書はこれです」

サーバーから CertificateRequest メッセージが送られなかった場合には Certificate メッセージは送られません。

(5)でサーバーから CertificateRequest メッセージを送られていた場合、クライアントは、 Certificate メッセージと共に自分の証明書をサーバーに送ります。
サーバーは、クライアントの証明書を読んで検証します。

#####(8) ClientKeyExchange (クライアント→サーバー)

クライアントが ClientKeyExchange メッセージを送ります。

クライアント「これが暗号化したプレマスターシークレットです」

暗号スイートが RSA を用いる場合には、 ClientKeyExchange メッセージと共に暗号化したプレマスターシークレットが送られます。
暗号スイートが Diffie-Hellman 鍵交換を用いる場合には、 ClientKeyExchange メッセージと共に、 Diffie-Hellman の公開鍵が送られます。

プレマスターシークレットとは、クライアントが作った乱数で、あとからマスターシークレットを作るための種の役割をするものです。
この値は、サーバーの公開鍵で暗号化されてからサーバーへ送られます。

#####(9) CertificateVerify (クライアント→サーバー)

クライアントが CertificateVerify メッセージを送ります。

クライアント「私は確かにクライアント証明書の本人です」

クライアントが CertificateVerify メッセージを送るのは、サーバーから CertificateRequest メッセージを受け取っていた場合です。このメッセージの目的は、クライアント証明書のプライベート鍵を確かにクライアントが持っていることを、サーバーに通知することです。

この目的のためクライアントは、「マスターシークレット」および「ハンドシェイク プロトコルでやりとりしたメッセージ」のハッシュ値をとり、それにデジタル署名を施したものをサーバーに送ります。

#####(10) ChangeCipherSpec (クライアント→サーバー)

クライアントが ChangeCipherSpec メッセージを送ります。

クライアント「では、暗号を切り替えます」

実は ChangeCipherSpec メッセージは、ハンドシェイク プロトコルのメッセージではなく、暗号仕様変更のプロトコルのメッセージになります。
この ChangeCipherSpec メッセージが送られる直前までに、暗号スイートに関する情報は、すべてクライアントとサーバー間でやりとりされていますので、このメッセージを合図にして、暗号を切り替えることができます。

これを境にして、 TLS レコード プロトコルは「合意した暗号を使った通信」に入ることになります。


#####(11) Finished (クライアント→サーバー)

クライアント「以上で、ハンドシェイク プロトコルを終わります。」

暗号はすでに切り替わりましたので、 Finished メッセージは、切り替え後の暗号スイートを使って送られます。
実際に暗号化を行うのは TLS レコード プロトコルです。

サーバーは送られてきた暗号文を復号化して、 Finished メッセージが正しく得られるかどうかを調べます。
これで、ハンドシェイク プロトコルが正常に完了し、正しく暗号スイートが切り替わったどうかを確認できるのです。

#####(12) ChangeCipherSpec (クライアント←サーバー)

今度は、サーバーが ChangeCipherSpec メッセージを送ります。

サーバー「では、暗号を切り替えます。」

#####(13) Finished (クライアント←サーバー)

クライアント同様、サーバーも Finished メッセージを送ります。

サーバー「以上で、ハンドシェイク プロトコルを終わります。」

このメッセージは、切り替え後の暗号スイートを使って送られます。
実際に暗号化を行うのは TLS レコード プロトコルになります。

#####(14) アプリケーション データ プロトコルと TLS レコード プロトコルを用いて、暗号化通信を行うことになります。

# やってみる

まず、 Wireshark をクライアント PC にインストールしてください。

URL:https://www.wireshark.org/#download

TLS ハンドシェイクのパケットを取得するのは実に簡単です。
HTTPS サイトにブラウザーアクセスするだけです。

試しに Azure ポータルにアクセスしてみましょう。

URL:https://portal.azure.com/

その際に Wireshark のパケット取得を開始しておいてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/68b7c9d0-1e66-c9ff-24b3-de012529e60b.png)



アクセスしたら、パケット取得を停止します。これだけです。

# TLS ハンドシェイクの中身を見てみよう

#####1. まず、全部のパケットを 1 行 1 行見るのは大変なので、 Fileter しましょう。
   フィルター名：tcp.port == 443

#####2. 次に、今回は TLS ハンドシェイクの流れを見たいので 、Protocol 列でソートして、 TLS v1.2 を見るようにします。

上記で絞り込むと、下記たった 4 行の中に、上記で説明した TLS ハンドシェイクの一連の流れが詰まっています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/4e25733d-d067-0b8c-c753-363126a93418.png)


## 細かく見ていきます

#####(1) ClientHello (クライアント→サーバー)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/f99256ca-e685-11cf-e842-679cc5aa0628.png)



赤枠で囲った部分に下記情報(時刻は除く)が記載されているのが分かりますね。

・使用するバージョン情報
・現在時刻
・サーバー ランダム
・セッション ID
・使用する暗号スイート
・使用する圧縮方法

#####(2) ServerHello (クライアント←サーバー)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/99318125-fb73-20e8-f1df-0093e0c5b0e8.png)


上記のとおり、クライアントからの要求に応じて、 TLS 1.2 で通信しましょう、と返しているのがわかりますね。

・使用するバージョン情報
・現在時刻
・サーバー ランダム
・セッション ID
・使用する暗号スイート
・使用する圧縮方法

#####(3) Certificate (クライアント←サーバー)

下記のとおり、 2 種類の証明書がクライアント側に提示されていることが分かります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/9dee37c0-0617-280b-0536-2334f8a758a6.png)



も少し細かく見ていくと、 SHA-2 の 256 bit で暗号化している、とか、 CyberTrust 社が発行している証明書を使っている、などの情報が細かく載っています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/bac0e02e-1540-3501-0a71-bf4ef12beda4.png)



#####(4) ServerKeyExchange (クライアント←サーバー)

サーバーが ServerKeyExchange メッセージを送っています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/6c37aa68-52fc-d961-3b26-7c58ea7f0939.png)



鍵交換をするための情報をクライアント側に送っていますね。

#####(6) ServerHelloDone (クライアント←サーバー)

サーバーからの一連のメッセージはこれで終了です、というメッセージになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/d061f82c-5551-e0b9-bdd9-89ad5aaeb1c5.png)



#####(8) ClientKeyExchange (クライアント→サーバー)

クライアントが ClientKeyExchange メッセージを送ります。
暗号スイートが Diffie-Hellman 鍵交換を用いているのが分かりますね。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/cbfce7d7-7fc7-a170-3491-884c94b7fa3d.png)




#####(10) ChangeCipherSpec (クライアント→サーバー)

クライアントが ChangeCipherSpec メッセージを送ります。

クライアント「では、暗号を切り替えます」
これを境にして、 TLS レコード プロトコルは「合意した暗号を使った通信」に入ることになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/59123c20-4008-94a7-0220-981dad08ecf9.png)



#####(11) Finished (クライアント→サーバー)

クライアント「以上で、ハンドシェイク プロトコルを終わります。」

Encrypted Handshake Message と表記されていますが、 Finished と同義です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/30c18348-2bb5-ae4c-4425-3db7740c2c6c.png)


#####(12) ChangeCipherSpec (クライアント←サーバー)

今度は、サーバーが ChangeCipherSpec メッセージを送ります。
サーバー「では、暗号を切り替えます。」
![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/ef42f959-eb4f-8e61-6154-c5703441a189.png)




#####(13) Finished (クライアント←サーバー)

クライアント同様、サーバーも Finished メッセージを送ります。

サーバー「以上で、ハンドシェイク プロトコルを終わります。」

Encrypted Handshake Message と表記されていますが、 Finished と同義です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/373423/49dd9a57-528d-ae29-c201-9cd7bf7435f9.png)



#まとめ
正直私も今まで全く意識できていなかったのですが、こうやって Wireshark を使うことで、誰もが簡単に、 TLS ハンドシェイクの流れを垣間見ることができます。

今回の内容が少しでもお役に立てれば幸いです。