DH鍵交換でもWiresharkでSSL/TLSを復号化したい

WiresharkでSSL通信の中身を覗いてみる を拝見し、実際に自分の手を動かしてみたのですが、記事内でも触れられているようにDiffie-Hellman(DH) key exchange (および楕円曲線を用いたECDH) では、実際に鍵を送り合うわけではなく、鍵を生成するためのパラメータだけがやり取りされるので、サーバの秘密鍵を持っていても復号できません。
今回はDH鍵交換(以下、DHE)でもWiresharkSSL/TLSを復号すべく試行錯誤したので、まとめます。

事前の設定内容

VirtualBoxで以下のゲストOSやApacheを立ち上げておきます。

$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)

$ httpd -v
Server version: Apache/2.4.6 (CentOS)
Server built:   Nov 14 2016 18:04:44

$ rpm -aq | grep mod_ssl
mod_ssl-2.4.6-45.el7.centos.x86_64

$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

ホストOSとゲストOSのIPは、それぞれ以下のようにアサインしました。

ホストOS ゲストOS
192.168.10.100 192.168.10.104

オレオレ証明書CentOS に Apache HTTPD を導入して SSL を有効にする を参考に作成し、証明書と秘密鍵は以下の名前でssl.confに記載しています。

# サーバ証明書
SSLCertificateFile /etc/httpd/conf/server.crt
# サーバ秘密鍵
SSLCertificateKeyFile /etc/httpd/conf/server.key

(参考)DHEを無効にする

参考までにサーバ側とクライアント側でDHEを無効にすることでSSL/TLS通信を復号する方法も記載します。 この方法ではサーバの秘密鍵があれば、SSL/TLS通信を復号することができます。サーバの秘密鍵を外へ出したくないということであれば(むしろ出すべきでないと思いますが)、session keyのみをexportすることでも復号できます。 セッションキーをエクスポートし、秘密鍵を共有することなくWireshark でSSL/TLS 通信を複合 に両方のやり方がありますので、興味がある方はぜひ。

サーバ側でDHEを無効にする

WiresharkでSSL通信の中身を覗いてみる にも記載がありますが、ssl.confで以下のように設定すればOKです。

SSLCipherSuite kRSA

設定後はservice httpd restartApacheを再起動しておきます。 この状態でhttpsの通信を発生させ、Server Helloに含まれるcipher suiteを見ると Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)となっており、DHEもECDHEも使われていないことがわかります。

f:id:cipepser:20170326204102p:plain

ssl.confの設定を戻すことをお忘れなく

クライアント側でDHEを無効にする(Firefox)

Firefoxを立ち上げ、アドレスバーにabout:configと打ち込みます。 dheで検索し、DHEおよびECDHEを含むcipher suitesを無効化(false)にします。 以下の太字の箇所が変更箇所です(一番上以外)。

f:id:cipepser:20170326202640p:plain

この状態でhttpsの通信を発生させ、Server Helloに含まれるcipher suiteを見ると Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)となっており、サーバ側でDHEとECDHEを無効化したときと同じですね。

f:id:cipepser:20170326202929p:plain

Firefoxの設定を戻すことをお忘れなく

本題

さて、DHEを無効化せずにWiresharkで復号化したいですね。 復号化するにはサーバの秘密鍵ではなく、session keyを入手する必要があります。 まずはホストOSでsession keyをexport先を指定してFirefoxを起動します。 ここではpremaster.txtにexportさせます。
Firefoxは閉じた状態で実行しないと怒られます。

$ SLKEYLOGFILE=/YOURPATH/TO/premaster.txt "/Applications/Firefox.app/Contents/MacOS/firefox-bin"

上記を実行すると、いつも通りFirefoxが起動するので、httpsの通信を発生させます。 するとpremaster.txtには以下のようにsession keyが書き込まれているはずです。

$ cat premaster.txt
# SSL/TLS secrets log file, generated by NSS
CLIENT_RANDOM  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

※実際にはxが16進数で表示されるはずです。

この時点では以下のように復号化できていないです。

f:id:cipepser:20170326212249p:plain

復号するためには、Wiresharkpremaster.txtからsession keyを読むように指定してあげます。 指定方法は、WiresharkPreferenceからProtocolsSSL(Pre)-Master-Secret log filenamepremaster.txtを指定します。
※特にサーバの秘密鍵などは指定する必要ありません。

f:id:cipepser:20170326213212p:plain

OKを押下すると以下のようにhttpのパケットが復号できます。

f:id:cipepser:20170326214605p:plain

このときのServer Helloに含まれるcipher suiteを見ると Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)となっており、ECDHEでも復号化できていることがわかります。

f:id:cipepser:20170326214620p:plain

References