tlsハンドシェイクのメモ

tlsハンドシェイクとかわけわかめだったのでメモ。

やったこと

1. TLSの俺俺証明書作成

1
2
3
$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt

2. dokcerでapacheたてて、tcpdumpでパケットキャプチャー

apache用dockerコンテナー

https://github.com/kuniiskywalker/docker-test-capture-https

apache設定内容

DH鍵交換(DHE)無効

クライアントとサーバーで一時的な秘密鍵を生成して共通鍵を交換する安全な方法ですが、wiresharkで復号できないためRSAを使った鍵交換のみに設定。
但しクライアント側でもDHEを無効にしないと意味ない。

1
2
# SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLCipherSuite kRSA
SSLのキャッシュ無効

キャッシュを利用されると二回目以降とかの通信でhttpsの通信の一部を省略されてしまうため無効

1
2
3
4
#SSLSessionCache         "dbm:/usr/local/apache2/logs/ssl_scache"
#SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCache none
SSLSessionCacheTimeout 300

3. クライアントからcurlでアクセス

ブラウザの設定でDHEを無効にするのが面倒だったので、curlで検証しました。。。

1
2
# dokcerが動いてるvmによって以下アクセス先は要変更
curl --cacert server.crt https://localhost

4. キャプチャしたパケットをwiresharkで眺める

Wiresharkを開き、キャプチャしたファイルを開く。

通信が開始されるまでの大まかな流れ

Hello交換

事前にこういう方式で暗号化のやりとりしますよという取り決め

鍵交換

暗号化に使う鍵の交換
(実際はパラメーターを送信しあってお互いに共通の鍵を作る)

データ通信

利用開始!
実際は暗号化されていて中身は暗号化されていて、よくわからない文字列で埋め尽くされている

1.Hello交換

ざっくり言うと私(クライアント)が暗号化方式とか諸々提示しますので、あなた(サーバー)がどうやって暗号化するか決めてください。

というやりとりっぽい。

◯client hello

プロトコルバージョン

TLS1.0, 1.1, 1.2などの接続に用いるプロトコルを提示

(クライアント:TLS1.2で接続でお願いします!)

疑似乱数

クライアント側で生成した乱数をサーバーに送信

(クライアント:後で鍵交換に使う文字列なので控えといてください!)

暗号化方式

候補値(複数可)を提示

(クライアント:暗号化方式をこの中からお選びください!)

圧縮方式

候補値(複数可)を提示

(クライアント:圧縮方式は無圧縮でお願いします!)

※過去に色々問題があったため無圧縮一択らしい

拡張

候補値(複数可)を提示

(クライアント:その他もろもろの拡張に使用するものはこの中からお選びください)

※拡張に関しては割愛します

◯server hello

プロトコルバージョン

クライアントから提示された接続に用いるプロトコルに合意

(サーバー:TLS1.2で接続了解しました!)

疑似乱数

サーバー側で生成した乱数をクライアントに送信

(サーバー:後で鍵交換に使う文字列なので控えといて!)

暗号方式(暗号スイート CipherSuite)

提示された暗号化方式を選択

(サーバー:TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)でOKです)

  • TLS_RSA:サーバーのRSA鍵を使った鍵交換アルゴリズム
    今回は強制的にRSAを使う設定にしたのでこれが選択された模様

  • AES_256_GCM:共通鍵暗号のアルゴリズム
    TODO:後で調べる

  • SHA384:MAC(メッセージ認証)のアルゴリズム
    TODO:後で調べる

圧縮方式

提示された圧縮方式を選択

(サーバー:無圧縮でOKです)

◯Certificate

サーバーの証明書情報をクライアントへ。

◯Server Hello Done

Server Hello 終了

2.鍵交換

暗号化する際に使用する鍵を生成する。

◯Client Key Exchange

クライアントで生成したキーをサーバーに送信する。

鍵交換アルゴリズム

1. RSA

クライアント側で生成した共通鍵生成に必要な情報を、サーバー側の公開鍵を使って暗号化して、
サーバーに送信する。
悪い人に暗号化したデータをずっと保存されて、ある日サーバーの秘密鍵が漏洩してすべて解読されてしまうというリスクがある。

ClientKeyExchangeのみ行われる。

上記Certificateでサーバーから取得した認証情報から公開鍵を取り出し、「ランダムな文字列」を暗号化してサーバーに送信する。

以下の部分が暗号化された文字列

このPreMaster Secretの値を使って後述するクライアントとサーバーで使用する共通鍵を作成するっぽい。

2. DHE

サーバーの公開鍵ではなく、お互いに一時的に生成した秘密鍵/公開鍵のペアで公開鍵を送り合って、
共通鍵生成に必要な情報を暗号化して送り合う。
ServerKeyExchangeとClientKeyExchangeの2回の通信で鍵交換を行います

一時的に作成される鍵なのでサーバーの秘密鍵が漏洩しても安心!

数学苦手だけど、以下みたいな感じです。

これでお互いに安全に秘密値(PreMaster Secret)を送り合えます。


DHEだと一時的に作成した鍵を使用するので復号が面倒くさい。
今回は簡単に復号できるRSAを使用します。

(上記server helloの暗号化方式でTLS_RSAに合意をしている)

共有秘密鍵の算出

これまでのやりとには、鍵を生成するための前準備でした。
これでクライアントとサーバー側で通信を暗号化するために使う鍵を生成するための情報が出揃いました。

ECDHEというアルゴリズムで算出してるみたいですが、割愛します!
ざっくりいうと以下の情報でいい感じに鍵を作るってこと見たいです。

・Hello Clientでクライアントが生成した乱数
・Server Helloでサーバーが生成した乱数
・PreMaster Secret

◯Change Cipher Spec

新しい暗号化仕様の利用開始を通知

これでTLSハンドシェイクは終了

2.データ通信

これで晴れて暗号化通信が出来るわけです。

暗号化されたデータ通信

リクエストデータ

レスポンスデータ

通信内容の復号化

上記のままだと暗号化されててわけわかめだから、以下の設定で事前に作成した証明書をインポートすることでTLS通信内容が復号化でき中身が確認できる。

Preference->Protocols->SSL->RSA Key List->Editを選択し、以下を設定。

IP Address-> 172.17.0.2
Port->443
Protocol->http
Key File->俺俺証明書key(server.key)

リクエストデータ

レスポンスデータ

雑感

ちょーざっくりメモでした

Please share