Caloogy 文章
English

Diffie–Hellman 与离散对数:为什么要用素数阶子群

日期
2025 年 12 月 8 日
系列
密码学
阅读
4 分钟
主题
Diffie–HellmanDiscrete logCyclic groupPohlig–Hellman

一句话版本

两个陌生人在公开信道上各自出一个秘密指数 a,ba, b,交换 gag^a 和 gbg^b,就能算出只有彼此知道的共同密钥 gabg^{ab}。偷听者要算出它,基本上得先解离散对数:已知 gag^a 求 aa。 但如果群的阶只有小素因子,Pohlig–Hellman 算法会用中国剩余定理把问题拆成许多小问题逐个击破。所以必须在大素数阶的子群里做。

全文分三层:

  1. 通俗理解:混颜料
  2. 实际用处:TLS、Signal、VPN,以及 Logjam 攻击
  3. 数学原理:循环群、离散对数、Pohlig–Hellman、小步大步,附手算例子

第一层:通俗理解

1.1 混颜料

Alice 和 Bob 想在一个所有人都能偷听的房间里约定一个秘密颜色:

  1. 两人公开约定一种公共颜色:黄色;
  2. Alice 偷偷选红色,Bob 偷偷选蓝色;
  3. 各自把秘密色混进黄色:Alice 公开"黄+红"(橙),Bob 公开"黄+蓝"(绿);
  4. Alice 拿到绿,再混进自己的红:黄+蓝+红;Bob 拿到橙,再混进自己的蓝:黄+红+蓝;
  5. 两人得到同一种棕色。

偷听者看到了黄、橙、绿,但混好的颜料分不开,所以算不出棕色。

比喻 数学
公共颜色 公开的群和生成元 gg
秘密颜色 私钥 aa、bb
混颜料 求幂 gag^a
颜料分不开 离散对数难解
共同的棕色 共享密钥 gabg^{ab}

1.2 1976 年的革命

在此之前,两个人要加密通信,必须事先见面交换密钥(外交信使、密码本)。Diffie 和 Hellman 在 1976 年的论文《密码学的新方向》里第一次公开说明:陌生人可以在公开信道上凭空商定密钥。这是公钥密码学的起点,两人因此获得 2015 年图灵奖。(后来解密的档案显示,英国 GCHQ 的 Malcolm Williamson 在几年前已经秘密发现了同样的方法。)


第二层:实际用处

Logjam 攻击(2015)

两个问题凑到了一起:

  1. 很多服务器仍支持 90 年代美国出口管制留下的 512 位"出口级"DH,攻击者可以诱导连接降级到它;
  2. 大量服务器共用同一个素数 pp。离散对数的数域筛法中,最耗时的预计算只取决于 pp。对一个 512 位的 pp 预计算一周之后,破解每个连接只要一两分钟。

研究者还估算,国家级机构有能力对几个最常用的 1024 位素数做同样的预计算。从此 1024 位 DH 被淘汰,主流转向 2048 位以上或椭圆曲线。

中间人攻击

纯 DH 不验证对方身份。攻击者可以分别和 Alice、Bob 各做一次 DH,在中间转发并偷看。所以实际协议一定会配合签名或证书(RSA、ECDSA、Ed25519)来确认"跟我握手的确实是对方"。


第三层:数学原理

3.1 协议

公开参数:素数 pp,和 Zp×\mathbb{Z}_p^\times 中的元素 gg。

Alice 公开信道 Bob
选秘密 aa 选秘密 bb
算 A=gaA = g^a A→A \to
←B\leftarrow B
算 B=gbB = g^b
算 Ba=gabB^a = g^{ab} 算 Ab=gabA^b = g^{ab}

正确性只用到指数律 (gb)a=(ga)b(g^b)^a = (g^a)^b。

3.2 离散对数为什么难

Zp×\mathbb{Z}_p^\times 是 p−1p-1 阶循环群(这是有限域乘法群的一般定理)。取生成元 gg,映射

Zp−1→Zp×,x↦gx\mathbb{Z}_{p-1} \to \mathbb{Z}_p^\times,\qquad x \mapsto g^x

是群同构:左边加法对应右边乘法。正方向用快速幂只要 O(log⁡p)O(\log p) 次乘法;反方向就是离散对数,没有已知的多项式时间经典算法。

算法 适用于 大致代价
暴力枚举 任何群 O(n)O(n)
小步大步、Pollard rho 任何群("通用算法") O(n)O(\sqrt{n})
Pohlig–Hellman 群的阶 nn 有小素因子 O(q)O(\sqrt{q}),qq 是 nn 的最大素因子
指标演算、数域筛 Zp×\mathbb{Z}_p^\times(利用整数可分解) 亚指数

椭圆曲线群没有好用的"分解"概念,指标演算类算法不适用,只剩 O(n)O(\sqrt{n}) 的通用算法。这就是 ECC 密钥能短得多的原因。

3.3 Pohlig–Hellman:为什么要素数阶

假设群的阶 n=q1q2⋯n = q_1 q_2 \cdots。要解 gx=hg^x = h:

  1. 对每个素因子 qiq_i,把两边同时取 n/qin/q_i 次方:(gn/qi)x=hn/qi(g^{n/q_i})^x = h^{n/q_i}。左边的底数阶只有 qiq_i,所以这只是一个 qiq_i 阶的小离散对数问题,解出 x mod qix \bmod q_i;
  2. 用 中国剩余定理 把所有 x mod qix \bmod q_i 拼成 x mod nx \bmod n。

代价由最大的素因子决定。所以 p−1p - 1 如果是很多小素数的乘积,离散对数就很容易。对策是选安全素数 p=2q+1p = 2q + 1(qq 也是素数),并且在 qq 阶子群里做。

用抽象代数的语言说,这就是 Zn≅Zq1×Zq2×⋯\mathbb{Z}_n \cong \mathbb{Z}_{q_1} \times \mathbb{Z}_{q_2} \times \cdots(qiq_i 两两互素时)这个同构的直接应用:大循环群拆成小循环群的直积,难题也被拆碎了。

3.4 手算例子

一次 DH 握手

p=23p = 23,g=5g = 5(Z23×\mathbb{Z}_{23}^\times 的生成元)。

  • Alice 选 a=6a = 6:A=56 mod 23=8A = 5^6 \bmod 23 = 8
  • Bob 选 b=15b = 15:B=515 mod 23=19B = 5^{15} \bmod 23 = 19
  • Alice 算 196 mod 23=219^6 \bmod 23 = 2;Bob 算 815 mod 23=28^{15} \bmod 23 = 2

共享密钥是 22。(已用 Python 验算。)

Pohlig–Hellman 破解 Alice 的私钥

偷听者知道 g=5g = 5、A=8A = 8,要解 5x≡8(mod23)5^x \equiv 8 \pmod{23}。群的阶 22=2×1122 = 2 \times 11。

求 x mod 2x \bmod 2:两边取 1111 次方。511≡22≡−15^{11} \equiv 22 \equiv -1,811≡18^{11} \equiv 1。所以 (−1)x=1(-1)^x = 1,xx 是偶数,x≡0(mod2)x \equiv 0 \pmod 2。

求 x mod 11x \bmod 11:两边取 22 次方。52=25≡25^2 = 25 \equiv 2,82=64≡188^2 = 64 \equiv 18。要解 2y≡182^y \equiv 18,只需试 1111 个值:26=64≡182^6 = 64 \equiv 18,所以 x≡6(mod11)x \equiv 6 \pmod{11}。

CRT 拼起来:x≡0(mod2)x \equiv 0 \pmod 2,x≡6(mod11)x \equiv 6 \pmod{11},得 x=6x = 6。

原本要试 2222 个值,现在只试了 2+112 + 11 个。规模大时差别是指数级的。(已用 Python 验算。)

3.5 小步大步法(baby-step giant-step)

即使阶是大素数 nn,也能用"空间换时间"做到 O(n)O(\sqrt{n}):令 m=⌈n⌉m = \lceil \sqrt{n} \rceil,把 xx 写成 x=im+jx = im + j。

  1. 小步:存下 g0,g1,…,gm−1g^0, g^1, \dots, g^{m-1} 这张表;
  2. 大步:依次算 h⋅g−m,h⋅g−2m,…h \cdot g^{-m}, h \cdot g^{-2m}, \dots,每算一个就查表,撞上时 hg−im=gjh g^{-im} = g^j,得 x=im+jx = im + j。

这就是为什么 256 位的群只提供 128 位安全:2256=2128\sqrt{2^{256}} = 2^{128}。


总结

层次 要点
直觉 颜料混起来容易,分开很难
代数 x↦gxx \mapsto g^x 是循环群同构,反方向就是离散对数
安全 阶的小素因子会被 Pohlig–Hellman + CRT 拆掉;通用算法也有 n\sqrt{n}
工程 用安全素数或椭圆曲线;配合签名防中间人;临时密钥保证前向保密