Diffie-Hellman鍵交換 — 盗聴されても秘密が漏れない理由

「敵がすべての会話を盗聴しているのに、どうすればあなたと私だけの秘密を作れるだろうか?」

1976年、二人の天才がこの矛盾した問いに答えを出しました。

>

>

 

この記事で扱うこと

  • Diffie-Hellman (DH)が登場した歴史的背景
  • ペンキの比喩で理解する直感的な動作原理
  • モジュラー指数と離散対数問題という数学的基盤
  • Pythonコードで直接実装してみる鍵交換
  • 中間者攻撃 (MITM)とECDHへの進化
  • TLS・SSHのような実務環境での活用

導入 — 会う前に秘密鍵をどう共有するか

暗号化における最大の悩みは、意外にも「暗号アルゴリズム」ではありません。本当に難しいのは、暗号化に使う鍵をいかに安全に共有するかです。

1976年以前、二人が秘密通信をするには、事前に直接会って同じ鍵を共有するか、信頼できる運び屋(courier)を通じて鍵が書かれた紙を渡す必要がありました。冷戦時代のスパイ映画を思い浮かべてみてください。

しかし、インターネット時代ではどうでしょうか?一度も会ったことのないサーバーとクライアントが、しかもすべての通信が盗聴可能な公開ネットワーク上で、どうやって二人だけの秘密鍵を作り出すことができるのでしょうか?

この矛盾に見える問題を解決したのが、Whitfield DiffieMartin Hellmanが提案したDiffie-Hellman鍵交換アルゴリズムです。これは現代のインターネットセキュリティの基礎を築いた発明であり、2015年に二人にチューリング賞をもたらした業績でもあります。


ペンキの比喩 — 5分で理解するDH

数学に入る前に、DHを最も直感的に説明するペンキの比喩から見ていきましょう。

  1. アリスとボブは公開で黄色いペンキを約束します。敵もこれを見ます。
  2. アリスは自分だけの秘密の色(赤)を黄色に混ぜてオレンジ色を作り、ボブに送ります。
  3. ボブも自分だけの秘密の色(青)を黄色に混ぜて緑色を作り、アリスに送ります。
  4. アリスは受け取った緑色に自分の秘密の色(赤)を混ぜます → 黄+青+赤
  5. ボブは受け取ったオレンジ色に自分の秘密の色(青)を混ぜます → 黄+赤+青

結果として、二人は同じ色を持つことになります。しかし、盗聴者は黄色、オレンジ色、緑色しか見ていません。すでに混ざってしまったペンキから元の秘密の色を分離することは事実上不可能であるため、盗聴者は最終的な秘密の色を知ることはできません。

この「混ぜるのは簡単だが、分離は難しい」という一方向性がDHの本質です。


数学的原理 — モジュラー指数と離散対数問題

ペンキの比喩を数学に置き換えると、次のようになります。

公開パラメータ (誰もが知る値)

  • 大きな素数 p
  • pの原始根 g (生成元、generator)

鍵交換手順

  1. アリスは秘密の値 aを選び、公開値 A = g^a mod p を計算してボブに送ります。
  2. ボブは秘密の値 bを選び、公開値 B = g^b mod p を計算してアリスに送ります。
  3. アリスは K = B^a mod p を計算します。
  4. ボブは K = A^b mod p を計算します。

二人が得る値はどちらも g^(ab) mod p で正確に同じです。これが二人だけが知る共有秘密鍵 (shared secret)となります。

盗聴者は p, g, A, Bをすべて知ることができますが、A = g^a mod p から aを逆算する必要があります。この問題を離散対数問題 (Discrete Logarithm Problem, DLP)と呼び、十分に大きな p(通常2048ビット以上)では現実的な時間内に解くことはできません。

掛け算は簡単だが素因数分解は難しいというRSAの直感と似ています。DHは「指数計算は簡単だが、その逆(離散対数)は難しい」という非対称性を活用します。


Pythonで直接実装してみる

直感を固めるために、小さな数字で実際に動かしてみましょう。

# 教育用例 — 実際の運用には絶対に使用禁止
p = 23      # 公開素数 (実務では2048ビット以上)
g = 5       # 公開生成元

# アリスの秘密値
a = 6
A = pow(g, a, p)   # g^a mod p
print(f"앨리스가 공개하는 값 A = {A}")

# ボブの秘密値
b = 15
B = pow(g, b, p)   # g^b mod p
print(f"밥이 공개하는 값 B = {B}")

# 各自共有秘密鍵を計算
K_alice = pow(B, a, p)   # B^a mod p
K_bob   = pow(A, b, p)   # A^b mod p

print(f"앨리스의 공유 키: {K_alice}")
print(f"밥의 공유 키:     {K_bob}")
assert K_alice == K_bob

実行すると、二人が同じ鍵2を得ることを確認できます。

実務では、直接実装せず、検証済みのライブラリを使用する必要があります。以下は、cryptographyライブラリで安全なDH鍵交換を実行する例です。

from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

# 1) 公開パラメータの生成 (通常、サーバーが1回生成後に再利用)
parameters = dh.generate_parameters(generator=2, key_size=2048)

# 2) アリス、ボブそれぞれが鍵ペアを生成
alice_priv = parameters.generate_private_key()
bob_priv   = parameters.generate_private_key()

# 3) 公開鍵交換後に共有秘密を導出
shared_alice = alice_priv.exchange(bob_priv.public_key())
shared_bob   = bob_priv.exchange(alice_priv.public_key())
assert shared_alice == shared_bob

# 4) 生の秘密はそのまま使わず、KDFで対称鍵を導出
derived_key = HKDF(
    algorithm=hashes.SHA256(),
    length=32,
    salt=None,
    info=b"handshake data"
).derive(shared_alice)

ここで一つ重要な点は、DHが生成した共有秘密(raw shared secret)をそのまま対称鍵として使用しないということです。分布が均一でない可能性があるため、HKDFのような鍵導出関数(KDF)を通じて精製した後に使用する必要があります。


⚠️ 注意事項 — DHは万能ではない

1. 中間者攻撃 (MITM)に脆弱です

DHは相手が誰であるかを検証しません。攻撃者マロリーがアリスとボブの間に割り込むと、次のようなことが起こります。

  • アリス ↔ マロリー: 共有鍵 K1 確立
  • マロリー ↔ ボブ: 共有鍵 K2 確立
  • マロリーはすべてのメッセージをK1で復号 → 内容確認/改ざん → K2で再暗号化して転送

したがって、実務では必ずデジタル署名、証明書 (PKI)、事前共有鍵などで相手の身元を認証する必要があります。TLSではサーバー証明書がその役割を果たします。

2. 弱いパラメータは致命的です

2015年に発表されたLogjam攻撃は、多くのサーバーが512ビットの短いDHパラメータを使用していた点を狙いました。また、十分にランダムな秘密値 a, bを使用しないと、推測攻撃に晒されます。最低2048ビット以上の安全な素数グループ(RFC 7919のffdheグループなど)を使用することが安全です。

3. 静的DH vs 一時的DH (DHE)

鍵ペアを一度作成して継続的に再利用する静的DHは、秘密鍵が一度漏洩すると過去のすべての通信が復号されます。一方、セッションごとに新しい鍵ペアを生成するDHE (Ephemeral DH)前方秘匿性 (Forward Secrecy)を提供します。TLS 1.3でDHEまたはECDHEのみが許可されているのもこのためです。

4. 量子コンピュータには脆弱です

離散対数問題はショアのアルゴリズムによって多項式時間で解かれます。十分に強力な量子コンピュータが登場すれば、DHは破られます。NISTが推進しているポスト量子鍵交換 (PQC)、特にML-KEM(旧Kyber)とDHを組み合わせたハイブリッド方式が次世代標準として定着しつつあります。


ECDH — より小さく、より速く、より強く

楕円曲線上で同じアイデアを実装したものがECDH (Elliptic Curve Diffie-Hellman)です。

項目 伝統的DH ECDH
基盤問題 離散対数 (DLP) 楕円曲線離散対数 (ECDLP)
同等強度鍵サイズ 2048ビット 256ビット
演算速度 遅い 速い
帯域幅 大きい 小さい

同じセキュリティ強度をはるかに短い鍵で達成できるため、現代のTLS 1.3、SSH、Signalプロトコルなど、ほぼすべての場所でECDHE(一時的ECDH)が標準として定着しています。


✅ まとめ / 終わりに

  • Diffie-Hellmanは、公開チャネルで秘密鍵を合意することを可能にする最初の公開鍵アルゴリズムです。
  • 核となる原理は、g^(ab) mod p という対称的な結果と、離散対数問題の計算上の困難さです。
  • DHは鍵交換のみを担当し、相手を認証しません。安全のためには、必ず署名・証明書と併用する必要があります。
  • 実務では、一時的な鍵を使用するDHE/ECDHEが標準であり、これはTLSの前方秘匿性を担っています。
  • 次のステップとして、TLS 1.3ハンドシェイクSignalプロトコルのX3DH、そして量子耐性鍵交換であるML-KEM (Kyber)を調べてみることをお勧めします。

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です