Wiresharkに秘密鍵を登録しても解読できないのにSSLKEYLOGFILEを使えば解読できる理由

秘密鍵をWiresharkに登録してもTLSの通信を復号できなかったのに、SSLKEYLOGFILEを使えば復号できる理由を考えてみました。Wiresharkを使ってTLS通信を復号しようとした記事は以下です。

秘密鍵をWiresharkに登録してもTLS通信を解読できない?
ubuntuでFirefoxのTLS通信をキャプチャする。

TLSのネゴシエーションの中で「鍵交換」に関するやり取りに注目して必要な部分を抜き出したものが以下のシーケンス図です。
※TLSのネゴシエーションのすべてではありません、省略しているものもあります。

図の中にある各やり取りの説明をします。

Client Hello
クライアントからサーバーに対してWEBブラウザが対応しているいくつかの暗号スイートを提示します。

Server Hello
クライアントから提示された暗号スイートの中からクライアント/サーバーの双方で使える一番強い暗号スイートをサーバーが選び、クライアントに通知します。ここで決められた暗号スイートに従い、以降の通信を行います。

Server Certificate
サーバー証明書をクライアントに通知します。サーバー証明書には公開鍵が含まれています。

Server Key Exchange
必要に応じて一時的な鍵をクライアントに通知します。Server Key Exchange は省略される場合があります。

Client Key Exchange
共通鍵を生成する鍵の元を先ほど受け取った公開鍵で暗号化してサーバーに通知します。

Change Cipher Spec(クライアント→サーバー)
鍵の元から共通鍵の生成ができたことをサーバーに通知します。

Change Cipher Spec(サーバー→クライアント)
鍵の元から共通鍵の生成ができたことをクライアントに通知します。

Application Data
やり取りしたいデータを共通鍵で暗号化して双方で通信を行います。データの復号にも共通鍵を使います。

[広告]

それでは、本題に入ります。

鍵交換方式がRSAでは解読できるのにECDHEだと解読できないのはなぜか?
SSLKEYLOGFILEを使った場合、鍵交換方式がECDHEでも解読できるのはなぜか?

まずRSAの鍵交換の方式から見ていきます。鍵交換にRSAを使った場合はクライアント/サーバーの双方で同じ鍵の元を共有します。鍵の元はClient Key Exchangeでクライアントからサーバーに送られています。そのためWiresharkで鍵の元のデータを盗み見することができれば共通鍵を得ることができます。鍵の元からクライアントやサーバーがやっているのと同じアルゴリズムで共通鍵を作るのです。

次にECDHEの方式です。ECDHEは Ephemeral Eliptic Curve Diffie-Helman の略です。ECDHEの最後の「E」がEphemeral(一時的)を意味します。ECDHEの場合は鍵の元をクライアントとサーバーの双方で生成し、鍵の元を使って計算した値をクライアントとサーバーで送り合います。つまり、クライアントとサーバーは別々に違う鍵の元を持ち、それを使った計算結果を相互に交換するので鍵の元自体はネットワークに流れません。計算結果は Server Key Exchange と Client Key Exchange で交換してします。(厳密にはもっと複雑なのかもしれませんが交換しあっているということです)。鍵の元を交換せず計算結果だけを交換しあったところで同じ共通鍵を作れるのか?という疑問もありますができるようです(前提としてクライアントとサーバーで共有しておく数値があらかじめある)。そのへんのアルゴリズムは難しいので書くことはできないのですが、大事なところは、通信を盗み見したところで鍵の元はわからない、ということです。盗聴をして「前提としてクライアントとサーバーで共有していた数値」および「計算結果」を取得し、アルゴリズムがわかったとしても、鍵の元を逆算で導き出すのは至難の業(ほとんど不可能)というわけです。

と言うことで、1点目の「鍵交換方式がRSAでは解読できるのにECDHEだと解読できないのはなぜか?」については、ECDHEだとWiresharkでパケットを盗み見しても共通鍵を導き出せないから、というのが理由になります。今は鍵交換にRSAを使おうとするサーバーはほとんどないです。古いクライアント端末ではRSAしか対応していない場合があり、そのような端末でも利用できるようにするためサーバー側もRSAを使用している、というケースはあるでしょう。一応、RSAは鍵の元をずっと使い続けるわけではなく、随時再作成はしているようです(一時的な鍵として使用している)。なお、ECDHEのような鍵交換方式はPFS(Perfect Forward Secrecy)と呼ばれていて、PFSは秘密鍵がわかってもそれだけでは暗号を解読できない性質のものを指しています。

では、2点目のSSLKEYLOGFILEを使った場合を考えてみます。秘密鍵をWiresharkに登録してECDHEの鍵交換のやりとりを盗み見しようとしたのと違って、2点目の場合はWEBブラウザが「クライアントの鍵の元」と「サーバーからの計算結果」を知っています。ですのでブラウザは共通鍵を生成できます。つまり、SSLKEYLOGFILEを環境変数として設定することでブラウザが共通鍵そのものをファイルに書き出してくれます。ファイルの出力先はSSLKEYLOGFILEで指定した場所になります。そのためWiresharkはそのファイルを取り込むことで共通鍵を手に入れることができ、それをもとにTLS通信を復号することができるのです。ただブラウザに共通鍵を出力する機能があれば良いのですが、そういう機能がない場合はこの手は使えません。ChromeとFirefoxでは対応しているみたいですね。

秘密鍵をWiresharkに登録してもTLS通信を解読できない?

WiresharkでTLS通信の中身を見るために秘密鍵を登録してみます。秘密鍵はサーバー側に登録されている鍵で、CentOSのApacheの場合はssl.confの「SSLCertificateKeyFile」に記載されている場所にあります。この秘密鍵をWireshark(ubuntu 18.04 LTSにインストール)に登録する手順は以下です。windowsの場合もだいたい同じだと思います。

Wiresharkを立ち上げてメニューバーの「編集」から「設定」を選択する。Wiresharkの設定画面が表示されるので左側の選択欄から「Protocols」→「SSL」と辿ると、画面の上部に RSA keys list があるので隣の「Edit」を押下する。

「Edit」を押下すると SSL Decrypt の画面が表示されるので、左下の「+」ボタンを押下する。IP address、Port、Protocol、Key Fileが入力できる状態になるので、各項目に入力を行う。

IP address・・・サーバーのIPアドレスを指定
Port・・・443と指定
Protocol・・・httpと指定
Key File・・・秘密鍵のファイルを指定
Password・・・パスワードを指定 ※秘密鍵にパスワードがある場合

入力ができたら、SSL Decrypt の画面の「OK」ボタンを押下、および、設定画面の「OK」ボタンを押下する。

Wiresharkに秘密鍵が登録できたのでキャプチャをしてみます。キャプチャを開始するにあたり通信のはじめから取得する必要があるため、Wiresharkのキャプチャを開始してからWEBブラウザを立ち上げます。WEBブラウザを立ち上げたあとにWiresharkでキャプチャを開始しても通信のはじめからにはならないので、注意してください。ここで「はじめから」にこだわっているのはWEBブラウザを立ち上げるとサイトとの鍵の交換が始まってしまい最初に行われる鍵交換の部分のキャプチャを取り損なってしまうからです。

それではキャプチャしてみます。実際に取得したキャプチャは以下ですが、アプリケーションデータを見てみると、、、復号されてませんでした(悲)。以下の赤枠で囲った「Encrypted Application Data」の部分です。

ここで何かと調べた結果、どうやらクライアントとサーバーの鍵交換の方式がRSAでないとキャプチャしたデータからWiresharkが共通鍵を取りだせないらしい。RSAでは鍵の元となる情報をクライアント側からサーバー側に送り、相互で同じ共通鍵を生成するとのこと。Wiresharkは秘密鍵を使って、その鍵の元を盗み見して同じ方式で共通鍵を生成しようとする。

実際に、どんな鍵交換方式を使っているのかキャプチャから確認してみました。TLSのネゴシエーションでは「Client Hello」と「Server Hello」を使って相互にやりとりする暗号スイートを決めています。暗号スイートとは、鍵交換、認証、共通鍵暗号、ハッシュ関数に何を使うかを決めるためにやり取りする情報です。今回の場合は以下でした。

Client Hello(クライアントからサーバーに提示しているクライアント側で使用可能な暗号スイートのリスト)

Server Hello(クライアントから受けっとた暗号スイートの中で、サーバーが対応していて、且つ、暗号強度が一番強いものを選んでクライアントに返す)

Sever Helloの内容を見てみると、TLSのネゴシエーションで暗号スイートを「TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256」に決めたようです。これは鍵交換方式が「ECDHE」、認証方式が「RSA」、共通鍵暗号方式が「鍵長128ビットのAESのGCMモード」、ハッシュ関数に「SHA-256」を使用するという意味です。つまり、鍵交換方式はECDHEを使っていてRSAではなかったです。そもそもRSAは秘密鍵が漏洩したらパケットをキャプチャして通信の中身を見られてしまうため強度の弱い方式として今はほとんど使われていないみたい。キャプチャしてみたけど、意味なかったです。。

なお、サーバー側がどんな暗号スイートに対応しているかは以下のサイトでチェックできます。ウェブサーバー名を入力して「Submit」ボタンを押下すれば暗号スイート以外にも対応しているSSL/TLSのバージョンなどを提示してくれます。サイトの安全性を総合的に判断してくれます。

https://www.ssllabs.com/ssltest/

今回使ってみたウェブサーバーの暗号スイートの診断結果は以下です。

以前は秘密鍵をWiresharkに登録すればTLS通信の中身を見れたのだろうけれども、今はもうこの方法はほとんど使えないと思います。このブログの別の記事で「ブラウザに共通鍵の情報をファイルに書き出させて、それをWiresharkに読み込ませる」ことでTLS通信の中身を覗いているので、そっちの方法でやってみてください。鍵交換方式が「RSA」以外でも解読できます。

追記:
RSAでは解読できるのにECDHEでは解読できない理由、および、SSLKEYLOGFILEを使えば解読できる理由を考察してみました。こちらまで。