見出し画像

【lwIP】Tips #2|TLS1.3通信をWiresharkで復号する方法|mbedTLS 3.x/4.x対応

(English Version Here.)

このnoteでは、lwIPで実際に起こり得る不具合・脆弱性・再現方法・最小修正の考え方やデバッグのノウハウをシリーズで解説しています。
▶ 日本語記事一覧はこちら:lwIPトラブル対策室|日本語記事一覧


■ こんな時に役立ちます

WiresharkでキャプチャしたのにTLS通信の中身が見えない。TLS 1.3に切り替えたら今まで使えていたキーログが使えなくなった。そんな経験はありませんか?

次のような場面で、この記事が役立ちます。

  • TLS 1.3接続でWiresharkに `Application Data` しか表示されず中身が見えない

  • MQTT over TLS 1.3 の通信異常の原因を調べたい

  • TLS 1.3 ハンドシェイクの途中で切断されるがどのメッセージで失敗しているか分からない

  • 証明書エラーで切断されているはずなのに、TLS 1.2のときのように平文のAlert(理由コード)が見えない

  • TLS 1.2 の `CLIENT_RANDOM` 形式ではTLS 1.3 の復号ができないと気づいた

  • mbedTLS 3.x または 4.x でキーログを出力する方法が分からない

  • TLS 1.3対応アプリケーションの開発・デバッグを効率化したい

lwIP + mbedTLS 3.x/4.x 環境で TLS1.3 通信をWiresharkで復号する手順を解説します。

※TLS 1.2 についてはシークレットの種類と出力手順が TLS 1.3 と異なるため、別記事(Tips #1)で解説しています。wolfSSL・OpenSSLは本記事の対象外です。


■ 対象環境

  • mbedTLSバージョン: 3.1.x以降 / 4.x

  • lwIPバージョン: 2.x

  • TLSバージョン: 1.3

本記事の主対象は、現在の組み込み向けSDKで採用が多い mbedTLS 3.1.x〜3.6.x です。4.0.x についても同一のコードでそのまま対応できます。


■ この記事で分かること

  1. TLS 1.3 ではハンドシェイクのどの範囲が暗号化され、TLS 1.2 と比べてWiresharkで何が見えなくなるのか(復号環境の重要性が一段と高まる理由)

  2. TLS 1.3 のシークレット構造が TLS 1.2 と根本的に異なる理由と、NSS Key Log 形式で1接続あたり4行必要な仕組み

  3. mbedTLS 3.1.x〜3.6.x、4.0.x でTLS 1.3キーログを出力するコールバックの実装方法

  4. Wiresharkで平文を確認する手順と、復号できない場合のトラブルシューティング(チェックリスト付き)


■ TLS 1.3はハンドシェイクから暗号化される

TLS 1.2では、ハンドシェイクの大部分が平文で流れていました。Wiresharkでキャプチャするだけで、復号しなくても次のように読み取れます。

TLSv1.2 Record Layer: Handshake Protocol: Client Hello
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
TLSv1.2 Record Layer: Handshake Protocol: Certificate      ← 証明書の中身が平文で見える
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Unknown CA)  ← 検証失敗時は理由コードも平文
  • サーバー証明書の内容(発行者・有効期限・SAN)

  • 選択された暗号スイートや鍵交換パラメータ

  • ハンドシェイクが失敗したときのAlertの理由コード(unknown_ca、bad_certificate など)

つまりTLS 1.2では、「ハンドシェイクのどこで・なぜ失敗したか」は復号しなくても分かることが多く、キーログが必要になるのは主にアプリケーションデータの中身を見たい場合でした。

TLS 1.3では、この前提が崩れます。RFC 8446の規定により、ServerHelloより後のハンドシェイクメッセージはすべて暗号化されます。同じ接続をキャプチャすると、こう見えます。

TLSv1.3 Record Layer: Handshake Protocol: Client Hello     ← 平文で見えるのはここと
TLSv1.3 Record Layer: Handshake Protocol: Server Hello     ← ここまで
TLSv1.3 Record Layer: Change Cipher Spec                   ← 旧バージョン互換のためのダミー
TLSv1.3 Record Layer: Application Data
TLSv1.3 Record Layer: Application Data

`Application Data` と表示されているレコードの実体は、暗号化された EncryptedExtensions・Certificate・CertificateVerify・Finished です。サーバー証明書すら平文では見えません

さらに重要な点として、証明書検証エラーなどハンドシェイク失敗時のAlertも暗号化されます。クライアントが証明書を拒否してAlertを送るのはハンドシェイク鍵の確立後なので、TLS 1.2のように「Description: Unknown CA」が平文で読めることはなく、中身不明の暗号化レコードにしか見えません。

整理すると、キーログによる復号の位置づけはこう変わります。

  • TLS 1.2:アプリケーションデータの中身を見るための手段

  • TLS 1.3:それに加えて、証明書・拡張・失敗理由など、ハンドシェイク自体のトラブルシュートに必須の手段

「ハンドシェイクの途中で切断されるが、原因がパケットから読み取れない」── TLS 1.3への移行でこの状況は確実に増えており、復号環境の重要性はTLS 1.2のときより一段と高くなっています。

TLS 1.2と1.3の可視性の違いとトラブルシュートへの影響

■ TLS1.3通信をWiresharkで復号する仕組み

Wiresharkでキャプチャした直後、TLS 1.3通信はこのように見えます。

Wireshark - 復号前:Application Data として表示される

前節で見たとおり、アプリケーションデータだけでなくハンドシェイクの大部分もこの「中身の見えない暗号化レコード」になっています。

NSS Key Log形式のキーログファイルをWiresharkに設定すると、暗号化されていた通信の中身(ハンドシェイクメッセージを含む)が平文で読めるようになります。

Wireshark - 復号後:HTTPの内容が平文で表示される

TLS 1.2 のキーログは1接続につき1行です。

CLIENT_RANDOM <ClientRandom 32バイト hex> <MasterSecret 48バイト hex>

TLS 1.3 では鍵導出の仕組みが根本的に変わり、フェーズごとに異なるシークレットが生成されます。Wiresharkが復号するには最低4行のキーログが必要です。

CLIENT_HANDSHAKE_TRAFFIC_SECRET <ClientRandom 32バイト hex> <secret Hash長 hex>
SERVER_HANDSHAKE_TRAFFIC_SECRET <ClientRandom 32バイト hex> <secret Hash長 hex>
CLIENT_TRAFFIC_SECRET_0         <ClientRandom 32バイト hex> <secret Hash長 hex>
SERVER_TRAFFIC_SECRET_0         <ClientRandom 32バイト hex> <secret Hash長 hex>

secretの長さは、ネゴシエートした暗号スイートのハッシュ関数に依存します。TLS_AES_128_GCM_SHA256 のようなSHA-256系では32バイト、TLS_AES_256_GCM_SHA384 のようなSHA-384系では48バイトになります。

TLS 1.2 の `CLIENT_RANDOM` 形式ではTLS 1.3 の通信は復号できません。

TLS 1.3の1接続で必要な4つの鍵

■ PCブラウザやcurlコマンドなら簡単

WiresharkでTLS 1.3通信を復号するにも、TLS 1.2 と同様に `SSLKEYLOGFILE` 環境変数を使う方法が標準的です。

WindowsやLinuxでChromeやFirefoxを使っている場合、`SSLKEYLOGFILE` 環境変数を設定してブラウザを起動するだけで、キーログが自動的にファイルへ出力されます。

Windowsの例(コマンドプロンプト):

set SSLKEYLOGFILE=C:\keylog.txt
start chrome

Linuxの例:

export SSLKEYLOGFILE=/tmp/keylog.txt
google-chrome &

あとはWiresharkの設定画面(編集 → 設定 → Protocols → TLS)で `(Pre)-Master-Secret log filename` にそのファイルを指定するだけです。`Application Data` だったパケットが平文で見えるようになります。

ChromeやFirefoxはTLSライブラリに `SSLKEYLOGFILE` の検出機能が組み込まれているため、環境変数を設定するだけで自動的にキーログを出力します。

curl(OpenSSLビルド版)でも同じ環境変数が使えます。

Windowsの例(コマンドプロンプト):

set SSLKEYLOGFILE=C:\keylog.txt
curl https://example.com

⚠️ 注意:Windows標準搭載のcurlは動作しない場合があります

Windows 10/11 に標準搭載されている `curl.exe` は、OpenSSL ではなく Windows 独自の Schannel バックエンドでビルドされています。Schannel は `SSLKEYLOGFILE` に対応していないため、環境変数を設定してもキーログが出力されません。

動作しない場合は、curl公式サイト(curl.se/windows) から OpenSSL ビルド版 の curl を入手してください。


■ 組み込みでは同じようにはできない

組み込み機器でlwIP + mbedTLSを使ってTLS通信を実装している場合、`SSLKEYLOGFILE` 環境変数は使えません。

mbedTLSでTLS 1.3のキーログを出力するには、ハンドシェイク中に各シークレットが確定した瞬間に呼び出されるコールバック関数を実装し、シリアル UART などに出力する処理を追加する必要があります。実装方法はmbedTLSのバージョンによって異なります。


■ 有料パートについて

TLS 1.3では、復号環境がなければアプリケーションデータどころか「ハンドシェイクがなぜ失敗したのか」の特定すら困難です。組み込み機器のTLSを1.3へ移行するなら、キーログ出力はデバッグ環境の必須装備と言えます。

有料パートでは、lwIP + mbedTLS 3.x/4.x のTLS 1.3通信を復号するための実装例を具体的に解説します。

またlwIPの全体像や、各ベンダーSDKごとの差異、なぜlwIPの不具合情報が重要なのか、といった背景については、導入記事で整理しています。

【lwIP】よくある不具合と対策まとめ|組み込みTCP/IPトラブル事例集


■ 実装手順

mbedTLS のバージョンによって実装手順が一部異なります。

ここから先は

9,122字 / 1画像

¥ 800

Amazon Payで支払うと最大2%還元のチャンス! 9/30まで

この記事が気に入ったらチップで応援してみませんか?