合同会社小村ソフト
第 3 章

鍵合意 — Diffie–Hellman を手計算する

盗聴されている経路の上で共通鍵を作る Diffie–Hellman 鍵合意を、小さな素数で手計算して確かめる。ECDHE と前方秘匿性まで。

盗聴されている経路の上で、共通鍵を作る

第 2 章の宿題を思い出してください。本文を守る共通鍵暗号は速いが、最初の共通鍵をどう共有するかが問題でした。しかも前提は厳しく、ブラウザとサーバの間の経路は盗聴されているかもしれません。「盗聴されている経路の上で、盗聴者には分からない共通の秘密を作る」— 一見不可能に聞こえるこの問題を解くのが、Diffie–Hellman(DH)鍵合意です。

この章の目標はただ 1 つ、これを自分の手で計算して納得することです。必要な数学は「べき乗」と「割り算の余り(mod)」だけ。17 mod 5 = 2(17 を 5 で割った余りは 2)が分かれば準備完了です。

手順は 4 ステップ

あなたとサーバの 2 人で鍵を作ります。まず 2 人は、公開の場で 2 つの数を決めます — 素数 p と、その相棒 g。ここでは手計算できるように p = 23, g = 5 とします(実物は p が数百桁になるだけで、手順は同じです)。

Step 1 — 秘密の値を選ぶ
あなたは秘密の数 a を選ぶ(例: a = 6)。サーバも秘密の数 b を選ぶ(例: b = 15)。これは誰にも送らない
Step 2 — 公開値を計算して交換する
あなたは A = ga mod p = 56 mod 23 = 8 を送る。サーバは B = 515 mod 23 = 19 を送る。この 2 つは盗聴されて構わない
Step 3 — 相手の公開値を、自分の秘密でべき乗する
あなた: s = Ba mod p = 196 mod 23 = 2。サーバ: s = Ab mod p = 815 mod 23 = 2
Step 4 — 同じ値になったものを共通鍵の種にする
どちらも gab mod p を計算しているので、必ず一致する。以降はこの値から作った共通鍵で高速に暗号化する

大きなべき乗にひるむ必要はありません。2 乗を繰り返して、そのつど mod を取るのがコツです。たとえば 56 mod 23 は、52 = 25 ≡ 2 → 54 = (52)2 ≡ 4 → 56 = 54 × 52 ≡ 4 × 2 = 8。途中の数が 23 未満に保たれるので、暗算でも追えます。

両者が公開値を交換し、それぞれ手元で同じ共有秘密に到達する Diffie–Hellman 鍵合意の図

経路を流れるのは公開値 A と B だけ。秘密の値 a, b と共有秘密 s は、それぞれの手元から一度も外に出ません。

小問 3-1 — 鍵合意を自分の手で回す

公開パラメータは p = 23, g = 5 とします。本文の手順どおり、紙に途中経過を書きながら計算してください。

Q1. ウォーミングアップ: 53 mod 23 はいくつですか。

Q2. あなたの秘密の値を a = 6 とします。相手に送る公開値 A = 56 mod 23 はいくつですか。(ヒント: 52 mod 23 から積み上げる)

Q3. 相手から公開値 B = 19 が届きました。あなたの秘密の値 a = 6 で計算する共有秘密 s = 196 mod 23 はいくつですか。(ヒント: 19 ≡ −4 (mod 23) を使うと速い)

シミュレータで確かめる

下のシミュレータは p = 23, g = 5 の鍵合意を再現します。あなたの秘密 a とサーバの秘密 b を動かして、両者が別々に計算した共有秘密が必ず一致すること、そして盗聴者に見える欄には秘密が現れないことを確かめてください。

シミュレータの動作概要(テキスト版): a と b のスライダーを動かすと、公開値 A = ga mod p と B = gb mod p、およびあなた側の計算 Ba mod p とサーバ側の計算 Ab mod p が更新されます。a, b をどう選んでも 2 つの計算結果は同じ値になり、画面には「盗聴者に見える値(p, g, A, B)」と「見えない値(a, b, 共有秘密)」が色分けで表示されます。

なぜ盗聴者には分からないのか

盗聴者の手元には p = 23, g = 5, A = 8, B = 19 がそろっています。共有秘密を知るには、たとえば 5a mod 23 = 8 を満たす a を見つければよい — p = 23 なら総当たりですぐ見つかります。数が小さいから解けるのです。

実物の p は数百桁あります。べき乗して mod を取る「順方向」の計算は 2 乗の繰り返しで一瞬で終わるのに、結果から指数を逆算する「逆方向」(離散対数問題)は、知られているどの方法でも現実的な時間で終わりません。この順方向と逆方向の圧倒的な非対称性が、DH の安全性のすべてです。暗号化して隠しているのではなく、「逆算が間に合わない」ことで守っています。

実際の TLS 1.3 では、同じ考え方を楕円曲線という別の計算の場で行う ECDH が使われます。しかも秘密の値 a, b は接続のたびに新しく作って使い捨てます(ECDHE の E = ephemeral、一時的)。この使い捨てが効いてくるのが前方秘匿性です — 暗号文を全部記録していた攻撃者が、後年サーバの長期秘密鍵を手に入れても、使い捨ての値はとうに破棄されているので、過去のセッションの鍵は再現できません。

小問 3-2 — 盗聴者に何が見えて、何が見えないか

手計算した鍵合意を、今度は盗聴者の視点から眺め直します。

Q4. Diffie–Hellman 鍵合意の通信をすべて盗聴していた第三者に見えている値の組み合わせはどれですか。

Q5. 盗聴者は A = ga mod p を見ています。それでも共有秘密を計算できない理由として正しいものはどれですか。

Q6. TLS 1.3 の鍵合意は、接続のたびに使い捨ての値で行われます(ECDHE の E = ephemeral)。これで得られる「前方秘匿性」の説明として正しいものはどれですか。

ただし、まだ大きな穴が残っている

手計算を終えたいま、1 つ意地悪な質問をします。あなたはいま、誰と鍵を合意したのでしょうか。

DH が保証するのは「公開値を交換した相手と秘密を共有できた」ことだけです。その相手が本物のサーバなのか、経路に割り込んだ攻撃者なのかは、DH 単体では分かりません。攻撃者が間に入り、あなたとも本物のサーバとも別々に鍵合意をすれば、すべての通信を復号・再暗号化しながら盗み読めます(中間者攻撃)。次章の主役 — 証明書 — は、まさにこの穴をふさぐ道具です。

この章で持ち帰ること

  • DH 鍵合意は秘密を送らずに秘密を共有する。経路を流れるのは公開値だけ
  • 安全性の根拠は離散対数の逆算が間に合わないこと。順方向(2 乗の繰り返し)との非対称性が鍵
  • TLS 1.3 は使い捨ての値で鍵合意する(ECDHE)。これが前方秘匿性 — 将来の鍵漏えいから過去の通信を守る
  • DH は「誰かと」鍵を作れるが「誰と」かは保証しない。その穴を埋めるのが次章の証明書