アドレスバーの左端に出る鍵マークは、そのページとの通信が TLS で暗号化され、サーバーの証明書が検証を通ったことを表しています。ひとつの記号にまとまっているので、毎日何百回と目にしていても中身までは説明しづらい。連載の最終回は、この記号を openssl s_client で開いて、部品に分解します。新しい部品はもう出てきません。ここまでの5回で作った部品を、1本の通信に組み立てるだけです。

OpenSSL で手を動かして学ぶ暗号の基礎、第6回は TLS と HTTPS です。環境の準備は第0回、今日使う証明書は第5回で発行したものです。

openssl s_client で、本物の TLS を覗く

いきなり本番です。s_client は TLS のクライアントとして接続し、やりとりの中身を見せてくれるコマンドです。相手は前回に続き当ブログで、-brief を付けると要点だけが出ます。

echo | openssl s_client -connect torigoedesign.com:443 -brief
# CONNECTION ESTABLISHED
# Protocol version: TLSv1.3
# Ciphersuite: TLS_AES_256_GCM_SHA384
# Peer certificate: CN=www.torigoedesign.com
# Hash used: SHA256
# Signature type: rsa_pss_rsae_sha256
# Verification: OK

たった7行ですが、この連載の全員がここに立っています。

7行に、連載の部品が全員いる

上から順に指差します。

表示 正体 登場した回
Ciphersuite の AES_256 本文を運ぶ共通鍵暗号 第2回
Ciphersuite の SHA384、Hash used 改ざんを見張るハッシュ 第1回
Peer certificate サーバーの身分証 第5回
Signature type 身分証と握手を保証する署名 第4回
Verification: OK 信頼の鎖がルートまでつながった 第5回

暗号スイート(Ciphersuite)は「この通信で使う部品の組み合わせ」の名前です。TLS_AES_256_GCM_SHA384 なら、本文は AES の 256 ビット鍵で暗号化し(GCM は暗号化と改ざん検知を同時にこなす動作モードです)、握手の整合性は SHA-384 で確かめる、と読めます。教科書の用語の羅列に見えていた文字列が、部品の型番表に見えてくれば、この連載は成功です。

鍵の合意だけ、X25519 に進化している

ひとつだけ、第3回と違う部品が使われています。表示を1行足して見ます。

echo | openssl s_client -connect torigoedesign.com:443 2>/dev/null | grep "Temp Key"
# Peer Temp Key: X25519, 253 bits

第3回のハイブリッド暗号では、AES の鍵を RSA で包んで送りました。実際の TLS 1.3 は、鍵を送りません。X25519 という楕円曲線の仕組みで、クライアントとサーバーが使い捨ての材料を出し合い、それぞれの手元で同じ鍵を計算します。鍵そのものは一度も回線を流れず、通信が終われば捨てられる。第3回の注意点で予告した楕円曲線(EC)は、ここで働いていました。

鍵は、運ばない。
ブラウザ サーバー 材料 A(公開してよい) 材料 B(公開してよい) 手元で同じ鍵を作る 手元で同じ鍵を作る 材料しか見えない ブラウザ 手元で同じ鍵を作る 材料 A(公開してよい) 材料 B(公開してよい) 材料しか見えない サーバー 手元で同じ鍵を作る

鍵そのものは一度も回線を流れず、通信が終われば捨てられる。

とはいえ構図は変わっていません。重い荷物は共通鍵暗号が運び、その鍵の合意を公開鍵の仕組みが守る。第2回と第3回で組んだハイブリッドの、鍵の受け渡し部分が上等になった形です。

openssl s_server で手元に HTTPS を立てる

覗くだけでは終わらせません。第5回の server.key と server.crt で、自分のサーバーを立てます。

openssl s_server -key server.key -cert server.crt -www -accept 4443

別のターミナルから、まず普通に接続してみます。

curl https://localhost:4443
# curl: (60) SSL certificate problem: self signed certificate

第5回でブラウザに拒まれたのと同じ理由で、curl にも拒まれます。ここで -k という近道があります。

curl -sk https://localhost:4443 | head -2
# <HTML><BODY BGCOLOR="#ffffff">
# <pre>

つながりました。-k は証明書の検証を丸ごと無効にするフラグです。返ってきたのは s_server のステータスページで、暗号化された通信自体は完全に動いています。動いているからこそ危険です。検証を切った接続は、相手が本物でもイブでも同じように成功します。-k は自分の実験環境の外では使わない。連載を通じて一番実務的な注意かもしれません。

正攻法は、検証を切ることではなく、信頼の名簿に載せることです。openssl の検証コマンドで確かめられます。

openssl verify server.crt
# CN=localhost
# error 18 at 0 depth lookup: self-signed certificate
# error server.crt: verification failed

openssl verify -CAfile server.crt server.crt
# server.crt: OK

同じ証明書が、名簿(-CAfile)に載せた瞬間 OK になりました。ブラウザの警告画面で「例外を追加」する操作や、社内 CA の証明書を端末に配る作業は、この1行と同じことをしています。第5回の警告の正体は、結局この名簿の有無だけでした。

検証を切るか、名簿に載せるか。
curl -k ISRG Root ✓ DigiCert ✓ … ✕ 名簿を見ない。相手が誰でも成功 -CAfile server.crt ISRG Root ✓ DigiCert ✓ server.crt ✓ … 名簿に載せる。本物だけ OK curl -k ISRG Root ✓ DigiCert ✓ … ✕ 名簿を見ない。相手が誰でも成功 -CAfile server.crt ISRG Root ✓ DigiCert ✓ server.crt ✓ … 名簿に載せる。本物だけ OK

ブラウザの「例外を追加」も、社内 CA の配布も、右側と同じこと。左側は実験環境の外で使わない。

鍵マークの中身を、部品で言う

  1. サーバーは身分証(証明書、第5回)を見せ、その保証は署名(第4回)とハッシュ(第1回)でできている
  2. ブラウザは身分証の鎖を、同梱の名簿のルートまでたどって検証する(第5回)
  3. 双方は使い捨ての鍵を合意する。鍵を運ばずに済ませる進化はあったが、公開鍵の仕組みが守る構図は第3回のまま
  4. 以後の本文は共通鍵暗号(第2回)が運び、改ざんはハッシュの系譜(第1回)が見張る
  5. すべての鍵と乱数の素性は、第0回の openssl rand に始まる予測できない乱数
握手は、4ステップ。
ブラウザ サーバー 1 身分証(証明書)を見せる 第5回の証明書。その保証は第4回の署名と第1回のハッシュ 2 名簿で、鎖を根元まで確認 第5回 3 使い捨ての鍵を合意する 第3回の進化形。鍵そのものは運ばない 4 本文を共通鍵で運ぶ。改ざんはハッシュが見張る 第2回と第1回 ブラウザ サーバー 1 身分証(証明書)を見せる 第5回の証明書。その保証は 第4回の署名と第1回のハッシュ 2 名簿で、鎖を根元まで確認 第5回 3 使い捨ての鍵を合意する 第3回の進化形。鍵そのものは運ばない 4 本文を共通鍵で運ぶ。 改ざんはハッシュが見張る 第2回と第1回

新しい部品はひとつもない。ページを開くたびに、この4ステップが数十ミリ秒で組み上がる。

鍵マークは「このページは安全」という魔法の印ではありません。相手の身元が名簿までつながり、経路が暗号化されている。保証はそこまでで、その先のページが親切かどうかは別の話です。それでも、この5つが毎回、開くたびに数十ミリ秒で組み上がっていると知って眺める鍵マークは、昨日までとは少し違って見えるはずです。

第0回で閉じた教科書に戻る

第0回で、アリスとボブの図が分からなくなって教科書をそっと閉じた話をしました。

いまなら分かります。アリスの鍵ペアはあなたのターミナルで alice.pem と alice.pub というファイルになり、飛び交っていた鍵のマークは、公開鍵で包んだ AES の鍵であり、秘密鍵で作った署名であり、認証局の判が押された証明書でした。図の中の記号は、全部この6回で一度ずつ、あなたの手元を通っています。

閉じた教科書があれば、開き直してみてください。今度は最後まで読めます。