こんにちは、CSIG 所属の市川です。普段は FutureVuls という脆弱性管理サービスの開発・カスタマーサポートをしています。
【理論編】HTTPS通信の中身を見て、どのようにしてセキュアな通信が確立されるかを理解するの続編です。
TLSハンドシェイクの中身を順番に見ていくため、理論編で示したシーケンス図をチラ見しながら読んでいただけると理解が進みやすいかと思います。
また、TLSハンドシェイクについて理解を深めたい方は、あわせて理論編もお読みください。
本記事 (実践編) でやりたいこと
理論編では、以下の3点について解説してきました。
- 各アルゴリズムが何のために使われるかを整理する
- TLS1.2ハンドシェイクを、シーケンス図を用いて整理する
- 鍵交換方式について理解を深める
本記事 (実践編) では、「Wireshark」というツールを用いて TLS ハンドシェイクの中身を実際に覗いてみて、理論編の内容を確認していきます。
TLS1.3 だと、ハンドシェイクの序盤で通信が暗号化されるのですが、今回の環境では通信の復号がうまく行えず、ハンドシェイクの中身を平文で確認できませんでした。そのため、本記事では TLS1.2 の通信を扱います。
TLS1.3ハンドシェイクはTLS1.2ハンドシェイクとは大きく異なります。TLS1.3 について理解したい方は、プロフェッショナルTLS&PKI 改題第2版などの書籍などを参考にしてください。
では、いきましょう。
実践編
実践編では、具体的に以下について確認していきます。
- HTTP 通信では、平文がそのまま見えてしまうこと
- 理論編のシーケンス図で示した、TLS1.2 ハンドシェイクの各フェーズ (
Client Hello
,Server key exchange
等) で送信される情報 - HTTPS 通信ではデータが暗号化されており、平文の内容を確認できないこと
準備
通信の中身を見るための準備について簡潔に記載します。
不要な方は、通信の中身を見てみる まで飛んでください。
準備1: 証明書の作成
HTTPS 通信では、公開鍵が被証明者のものであることを証明するため、「証明書」を用います。
手元でHTTPS 通信するための準備として、自己証明書を作成しました。
具体的には、以下の手順を実行します。
- 秘密鍵の生成
- CSR (Certificate Signing Request, 証明書署名要求) の作成
- 自己証明書の作成
OpenSSL を使って証明書を作成します。証明書の作成方法については、 食べる!SSL! ―HTTPS環境構築から始めるSSL入門 などを参考にしました。
1-1. 秘密鍵の作成
以下コマンドで生成します。
openssl genrsa 2048 > /path/to/server.key |
openssl req -text < server.key
コマンドで、確認できます。
RSA Private-Key: (2048 bit) |
1-2. CSR の作成
以下コマンドで作成します。
openssl req -new -key /etc/~/server.key > ~~/server.csr |
以下が確認できます。
Certificate Request: |
1-3. 証明書の作成
以下コマンドで、証明書を作成します。
openssl x509 -req -signkey server.key -in server.csr -out server.crt -extfile SAN.txt |
準備2: サーバー立ち上げ
今回は Go を使ってサーバーの立ち上げを行いました。 https
フラグを指定することで HTTPS ポートが開かれるようになっています。
package main |
通信の中身を見てみる
準備が整ったので、通信の中身を見ていきましょう。
通信の中身を見るにあたり「Wireshark」というツールを用いました。ここからダウンロードできます。
HTTP 通信の場合
以下でサーバー立ち上げを行い、
go run main.go |
curl で GET リクエストを送ってみます。
curl -i http://localhost:8080 |
ここで、Wireshark でパケットを確認してみます。
サーバから返される Hello World!
というテキストが、平文のまま送信されていることが分かりますね。
HTTPS 通信の場合
サーバーを立ち上げた後、
go run main.go -https |
curl でリクエストを送ります。
curl --tls-max 1.2 --insecure -i https://localhost:8443 |
※ 自己証明書を使用している場合、 --insecure
オプションが必要です。
$ man curl |
Wireshark でパケットを確認してみます。
通信データが Encrypted Application Data
となっています。無事暗号化されていることが分かりますね。

本題: HTTPS 通信の際の TLSハンドシェイクを追ってみる
では、暗号化されるまでに、どんな情報がクライアントとサーバー間でやりとりされているかを、順を追って見ていきます。
必要に応じて、「理論編」の「シーケンス図」も参考にしていただければと思います。
再度 curl でリクエストを送ります。
curl --tls-max 1.2 --insecure -i https://localhost:8443 |
(1) Client Hello
クライアント側が、対応している暗号スイートの一覧をサーバーに送っていることが分かります。
また、ランダム文字列 Random
も一緒に送信していることが分かります。これはマスタシークレットの生成に使用されます。

(2) Server Hello
サーバーが、選択した暗号スイート (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
) をクライアントに伝えています。
- 鍵交換アルゴリズム :
ECDHE (Elliptic Curve Deffie Helman Ephemeral)
- 認証:
RSA
- ハッシュアルゴリズム:
SHA256
- 暗号化アルゴリズム:
AES128_GCM
また、ランダム文字列 Random
も一緒に送信していることが分かります。(1)と同様、マスタシークレットの生成に使用されます。

(3) Certificate
サーバー側がサーバー証明書をクライアントに送信しています。
- 証明書作成/検証のためのアルゴリズムとして
sha256WithRSAEncryption
が採択されていることが分かります - クライアントは、以下の手法でサーバ証明書の正当性を検証します。
- サーバの証明書からハッシュ値を生成する (
hash_a
とする) - この証明書に付属している電子署名を、公開鍵を用いて復号する (
hash_b
とする) hash_a
とhash_b
が一致することを確認する
- サーバの証明書からハッシュ値を生成する (

(4) Server Key Exchange
鍵交換アルゴリズムとして ECDHE を使用していることが確認できます。
Server Key Exchangeで、サーバーは named_curve (楕円曲線暗号の事前パラメータ群の名前) として x25519
を選択しています。Pubkey
(公開鍵) も同時に送信しています。(シーケンス図の公開鍵 B_pub に相当)
また、 signature
(電子署名) も送られてきていることがわかります。これは、 DH パラメータに対してサーバーの RSA 秘密鍵を適用することで作成された電子署名です。named_curve や pubkey が正当なサーバから送られてきていることを検証するために用いられます。
※ サーバ証明書に付属されている電子署名とは異なるものなので、混乱しないよう気をつけてください。

(6) Client Key Exchange
クライアント側も、Client Key Exchange で public key を送信しています。 (シーケンス図の秘密鍵 A_pub に相当)

(7)~(10): Change cipher spec, Finished
Change Cipher Spec で暗号化モードに切り替えます。
また、Finished の Verify Data
で、全メッセージのハッシュ値を用いて改ざん検知を行います。
(client 側のものだけ載せます)

暗号化後の通信
データが暗号化され、謎の文字列になっていることが分かります。やったね。

おまけ: HTTPS 通信を復号してみる
SSLKEYLOGFILE
を指定することで、プリマスタシークレットキーをファイルに書き出すことができます。
SSLKEYLOGFILE=/path/to/sslkeys.log curl --insecure --tls-max 1.2 -i https://localhost:8443 |
このプリマスタシークレットキーが書き出されたファイルを、以下のように Wireshark 内の設定で指定してあげることで、データの復号できます。

詳しくは Wireshark の Wiki や こちらの記事 をご覧ください。
すると、Protocolが TLSv1.2 → HTTP2 に変わっていることが分かります!
Hello World!
が get されていることが分かります。無事 (?) 復号されていますね。

まとめ
通信の中身を追ってみたことで、どのように SSL/TLS ハンドシェイクが行われ、セキュアな通信が確立されるかを、実感をともなって理解できました。
また、ハンドシェイク後、実際にデータが暗号化されていることも確認できました。
みなさんも、通信の中身を追ってみると色々と面白いことがわかるかもしれません。
おわり。
参考文献
(※ 理論編に載せてあるものと同じです)
⚪︎全体
SSL/TLS の仕組みを勉強する上で、以下の技術書が非常に参考になりました。
特に、1冊目は非常に体系的かつ網羅的にまとまっているので、SSL/TLS について勉強したい人は持っておいて損はないと思います。2冊目も分かりやすく、SSL/TLS について応用情報程度の知識しかなかった自分でも理解しやすかったです。自己証明書の作成なども、こちらの本を参考にしました。 (ただし、少し古めの本で、TLS1.3 については書かれていません。)
⚪︎ DHの理解
Diffie-Hellman 鍵交換方式については、以下を参考にしました。
- RFC 7748 - Elliptic Curves for Security 日本語訳
- TLS・SSLハンドシェイクの仕組みは? | Cloudflare
- 楕円曲線暗号のPythonによる実装その1(有限体とECDH鍵共有)
- RFC 7748 - Elliptic Curves for Security
- Standards for Efficient Cryptography | SEC 2: Recommended Elliptic Curve Domain Parameters
⚪︎ Wireshark
wireshake でハンドシェイクを確認する方法については、以下を参考にしました。