街角那家永远坐满人的咖啡馆里,爱丽丝和鲍勃正低声商量一件大事。糟糕的是,隔桌的伊芙总在假装看报纸,耳朵却竖得老高。任何写在纸条上的话、说出口的秘密,都可能被伊芙听去。爱丽丝和鲍勃想在公开的桌上“传递秘密颜料配方”,却不能让伊芙偷学——这该怎么做到? 这正是密码学里“密钥交换”要解决的根本问题:通信双方如何在完全公开、可能被窃听的信道上,协商出一把只有彼此知道的“密钥”。密钥是一切加密通信的起点。没有它,HTTPS、SSH、VPN 等安全协议都会轰然倒塌。 1976 年,Diffie 和 Hellman 提出了一个石破天惊的方案:两个素不相识的人,通过交换几个公开数字,就能在众目睽睽之下“变出”一把相同的密钥。而窃听者虽然全程看到了所有公开数字,却怎么也推不出这把密钥。这个故事背后是“离散对数”这道数学天堑;今天的互联网则更多使用它的椭圆曲线版本(ECDH),并配合身份认证、前向安全性等机制,构建出坚不可摧的安全通道。本文就带你一步步走进这个奇妙世界。

岗位关联

密钥交换协议是 HTTPS、SSH、VPN 等一切安全通信的基石。理解 DH/ECDH 的原理、弱点与工程演进,是密码工程与安全架构从业者的核心分水岭——它能帮你在真实系统中选对方案、避开致命设计缺陷。

目录
  1. 可视化演示
  2. 密钥交换概述
  3. Diffie-Hellman原理
  4. DH安全性分析
  5. 椭圆曲线ECDH
  6. 中间人攻击
  7. 前向安全性
  8. TLS密钥协商
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Diffie-Hellman密钥协商流程、中间人攻击原理及ECDH椭圆曲线密钥交换,交互切换理解密码学密钥交换核心机制与安全防护。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Diffie-Hellman密钥交换 Alice和Bob交换公钥计算共享密钥 离散对数问题保证安全性 g^ab mod p DH协议在不安全信道协商共享密钥

密钥交换概述

在咖啡馆的故事里,最常见的保密方式是什么?两人约定好同一瓶秘密颜料——比如“落日橙”。爱丽丝用这瓶颜料写密信,鲍勃也用同一瓶颜料解谜。这就是对称加密:加密和解密用同一把密钥。问题来了:爱丽丝和鲍勃一开始手中没有现成的“落日橙”,他们必须在伊芙的眼皮底下把这瓶颜料“运”到对方手里——可颜料在公开桌上一放,伊芙不就看到了吗?

这个“先有鸡还是先有蛋”的困境,就是密码学中最古老的难题之一:密钥分发问题。在对称加密世界里,发送端和接收端必须共享同一把密钥 K。密钥一旦泄露,所有密文都形同虚设。传统做法是“线下当面交接”——这在古代靠信使、靠密道,在现代则完全不现实:你不可能为了网购一件衣服,先坐飞机去和服务器管理员当面交换密钥。

于是安全设计者们定下了目标:通信双方要能在公开、不安全、可被窃听的信道上,协商出一个共同的会话密钥,同时保证这个密钥对窃听者不可知。这个目标催生了“非对称思想”——双方不需要直接传递密钥,而是各自掌握不同的“秘密成分”,通过数学运算在同一时刻得到相同的“最终产物”。

对称加密是人类最熟悉的锁,但“送钥匙”成了最棘手的问题。Diffie-Hellman 横空出世,它让钥匙本身就地生成、双方各自“调”出同一把,而无需在公开信道传递——下一节我们来看这个魔术。

EK(M)=C,DK(C)=M\displaystyle E_K(M) = C, \qquad D_K(C) = M
对称加密中,加密函数 E 和解密函数 D 使用同一把密钥 K:K 将明文 M 变成密文 C,又将 C 还原成 M。正因为密钥相同,如何安全地把 K 交给对方就成了致命难题。
  1. 加密运算加密算法 E 输入明文 M 与密钥 K,输出密文 C。在咖啡馆里,就是用“落日橙”颜料写好密信。
    EK(M)=C\displaystyle E_K(M) = C
  2. 解密运算解密算法 D 输入密文 C 与同一把密钥 K,还原出明文 M。鲍勃用“落日橙”解读密信。
    DK(C)=M\displaystyle D_K(C) = M
  3. 密钥一致性对称加密要求双方持有完全相同的 K。问题变成:K 本身怎么安全地送到对方手中?这就像需要把同一瓶秘密颜料在伊芙眼前搬运过去,必须另辟蹊径。
    KAlice=KBob\displaystyle K_{\text{Alice}} = K_{\text{Bob}}

Diffie-Hellman原理

现在想象一个全新玩法:咖啡馆桌上摆着一罐公开的“基础色浆”——比如柠檬黄,任何人都可以拿去用。爱丽丝从口袋里掏出一滴只有自己知道的“红色秘密色浆”,混进一杯柠檬黄里,调出一杯橙色,然后把橙色的杯子公开放到桌上。鲍勃也做同样的事:他从口袋里掏出一滴“蓝色秘密色浆”,混进自己的柠檬黄里,调出一杯绿色,也公开放在桌上。

接着神奇的事发生了:爱丽丝拿起鲍勃调好的绿色,悄悄加入自己的红色秘密色浆;鲍勃拿起爱丽丝调好的橙色,悄悄加入自己的蓝色秘密色浆。两人手中的颜料最终调出了完全一样的颜色——一种红蓝混合的“神秘紫”。而始终旁观的伊芙只看到了柠檬黄、橙色和绿色这三杯公开颜料,却永远无法复制出那杯“神秘紫”。这就是 Diffie-Hellman 的核心魔法。

换成数学语言,这个“混合颜料”的过程就是模幂运算。通信双方先约定两个公开参数:一个大素数 p 和它的生成元 g(相当于“柠檬黄基础色浆”)。Alice 随机选一个秘密数 a(红色色浆),计算 A = gᵃ mod p(橙色),发给 Bob;Bob 随机选秘密数 b(蓝色色浆),计算 B = gᵇ mod p(绿色),发给 Alice。接下来,Alice 算 K = Bᵃ mod p,Bob 算 K = Aᵇ mod p。由于指数运算的交换性,这两个 K 完全相等,而它正是共享密钥。

伊芙在咖啡馆里看到的全部公开数字就是 p、g、A、B——相当于四杯公开的颜料。她试图像化学家一样反推出红色色浆和蓝色色浆,却撞上了一堵数学高墙:离散对数问题。这正是下一节的安全核心。

K=Ba mod p=(gb)a mod p=gab mod p=(ga)b mod p=Ab mod p\displaystyle K = B^a \bmod p = \left(g^b\right)^a \bmod p = g^{ab} \bmod p = \left(g^a\right)^b \bmod p = A^b \bmod p
共享密钥 K 的推导链:Bob 的公开值 B 用 Alice 的秘密数 a 取幂,等价于 g 的 ab 次方,也等价于 Alice 的公开值 A 用 Bob 的秘密数 b 取幂。双方算出的 K 相同,窃听者只看到 A 和 B。
  1. 公开约定双方公开选择大素数 p 和生成元 g。这相当于把那罐“柠檬黄基础色浆”大摇大摆放在桌上,任何人可用。
    p, g(公开)\displaystyle p, \ g \quad (\text{公开})
  2. 各自保密Alice 随机选秘密数 a,Bob 随机选秘密数 b。两个数绝不出现在信道上。
    a, b(秘密)\displaystyle a, \ b \quad (\text{秘密})
  3. 计算并交换公开值Alice 算出 A = gᵃ mod p 并发送,Bob 算出 B = gᵇ mod p 并发送。伊芙能看到 A、B、p、g。
    A=ga mod p,B=gb mod p\displaystyle A = g^a \bmod p, \quad B = g^b \bmod p
  4. 同一杯紫色Alice 用 Bᵃ,Bob 用 Aᵇ,指数运算的交换律让两人殊途同归,得到 gᵃᵇ mod p。
    K=Ba mod p=Ab mod p=gab mod p\displaystyle K = B^a \bmod p = A^b \bmod p = g^{ab} \bmod p

DH安全性分析

伊芙在咖啡馆里当然不甘心。她把那几杯公开颜料带回自己的化学实验室,想通过色谱分析反推出爱丽丝和鲍勃各自加入的秘密色浆。问题是:把混合颜料分离出原色,理论上并非不可能,但实际操作极其漫长——如果颜料是特制的,需要几亿年才能分离成功。DH 的安全性,正是建立在这样一个“正向调色一分钟、逆向分离一亿年”的数学不对称性上。

这个“逆向分离”在数学上被称为离散对数问题。已知大素数 p、生成元 g 和公开值 A,想求出秘密指数 a,使得 gᵃ ≡ A (mod p)——这就是求离散对数。离散对数之所以难,是因为模幂运算(正向)可以用快速幂在毫秒级完成,而它的逆运算目前没有多项式时间算法:当 p 是 2048 位的大素数时,即使动用全世界所有超级计算机,暴力搜索的耗时也远超宇宙年龄。这正是 DH 协议的安全基石。

但要小心,这块基石要稳,参数选择必须讲究。首先,p 必须足够大(现代标准至少 2048 位),否则可用“小步大步攻击”(Baby-step Giant-step)在 O(√p) 步内暴力破解——如果 p 只有 64 位,个人电脑几分钟就能解出秘密数。其次,p 最好选择“安全素数”(p = 2q + 1,q 也是素数),并确保生成元 g 的阶足够大,避免落入小阶子群。这些细节如果在工程实现中偷懒,DH 协议就会从“堡垒”变成“纸糊的墙”。

伊芙的化学实验室里堆满了烧杯和色谱仪,但面对 2048 位大素数这道天堑,她的“逆向分离”实验只能在宇宙热寂之后才可能出结果。这就是为什么爱丽丝和鲍勃可以放心在公开桌上调颜料——数学,是最可靠的门锁。

给定 A≡ga(modp),求 a≡log⁡gA(modp)(计算上不可行)\displaystyle \text{给定 } A \equiv g^a \pmod p, \quad \text{求 } a \equiv \log_g A \pmod p \quad (\text{计算上不可行})
离散对数问题:模幂是“秒算”,离散对数是“亿年难解”。两者的巨大时间鸿沟,是 DH 协议所有安全性的来源。
  1. 正向易算给定 g、a、p,用快速幂算法可在 O(log a) 次乘法内算出 gᵃ mod p。混合颜料只需一秒钟。
    a↦ga mod p(高效)\displaystyle a \mapsto g^a \bmod p \quad (\text{高效})
  2. 逆向难求给定 g、A、p,求离散对数 a 目前没有多项式时间算法。最经典的暴力方法是小步大步攻击,复杂度约 O(√p),要求 p 必须大到令其失效。
    A↦log⁡gA mod p(困难)\displaystyle A \mapsto \log_g A \bmod p \quad (\text{困难})
  3. 参数红线p 取 2048 位以上安全素数、g 选择高阶级元素,才能确保离散对数难题成立;否则小步大步攻击可以直接拆穿伪装。
    p=2q+1, q∈P,∣p∣≥2048 bits\displaystyle p = 2q + 1, \ q \in \mathbb{P}, \quad |p| \ge 2048 \text{ bits}

椭圆曲线ECDH

传统的 DH 协议好用,但有个苦恼:为了安全,p 要 2048 位,密钥长、运算慢、消耗大——就像拿着一口巨大的铁锅在咖啡馆里调颜料,又沉又占地方。密码学家们一直在寻找更轻便的“调色盘”。1985 年前后,两位数学家(Miller 和 Koblitz)独立发现:如果把“颜料混合”的舞台从普通整数换成“椭圆曲线上的点”,同样能搭建起一模一样的密钥交换戏法,而且更精妙。

椭圆曲线是什么?它并不是一个标准的椭圆,而是在某个方程式(如 y² = x³ + ax + b)上排列的一组点。这些点之间有一种神奇的“加法”运算:曲线上任意两点 P、Q 可以“相加”得到曲线上第三个点 R。反复把一个点 G 与自己相加 n 次,记作 n·G,这就是“标量乘法”。关键来了:已知 G 和 n,求 n·G 非常快;但已知 G 和 n·G,反推 n——这就是椭圆曲线离散对数难题,比普通离散对数更难破解。因此,256 位的椭圆曲线密钥就能提供约 3072 位 RSA/DH 的安全强度,密钥更短、带宽更省、计算更快。

ECDH 的流程和 DH 如出一辙:双方先约定同一条椭圆曲线和基点 G;Alice 选私钥 a,计算公钥 P = a·G;Bob 选私钥 b,计算公钥 Q = b·G;双方交换 P、Q 后,Alice 算 a·Q,Bob 算 b·P——由于“标量乘法”的交换律,两者都会得到同一点 S = ab·G,这个点的 x 坐标就是共享密钥。

在椭圆这个“轻便调色盘”上,调制共享色的过程又快又省。如今你的手机银行、微信支付每一次安全连接,背后几乎都是 ECDH 在那一瞬间“调好”了密钥——它已经成为现代互联网默认的秘密颜料调制法。

P=a⋅G,Q=b⋅G,K=a⋅Q=b⋅P=ab⋅G\displaystyle P = a \cdot G, \quad Q = b \cdot G, \quad K = a \cdot Q = b \cdot P = ab \cdot G
ECDH 的共享点 K:a 倍的 Q 与 b 倍的 P 相等,均等于 ab 倍的基点 G。公开的只有 P、Q,想从 P 反推 a 需解椭圆曲线离散对数,难度远超普通离散对数。
  1. 公开设定选择一条安全的椭圆曲线(如 secp256r1、Curve25519)和基点 G,所有人可见。
    E:y2=x3+ax+b,G∈E\displaystyle E: y^2 = x^3 + ax + b, \quad G \in E
  2. 私钥与公钥Alice 的私钥 a 生成公钥 P = a·G,Bob 的私钥 b 生成公钥 Q = b·G。私钥是秘密色浆,公钥是公开颜料杯。
    P=a⋅G,Q=b⋅G\displaystyle P = a \cdot G, \quad Q = b \cdot G
  3. 交换公钥并计算交换 P 和 Q 后,各自执行一次标量乘法,利用交换律得到同一共享点。
    K=a⋅Q=a⋅(b⋅G)=ab⋅G=b⋅(a⋅G)=b⋅P\displaystyle K = a \cdot Q = a \cdot (b \cdot G) = ab \cdot G = b \cdot (a \cdot G) = b \cdot P

中间人攻击

前几节里,伊芙一直是个被动的旁观者,只能看着公开颜料干瞪眼。但有一天她灵机一动:既然我算不出共享色,那我为什么不直接插到两人中间?她跑向 Alice,说“我是 Bob!”;又跑向 Bob,说“我是 Alice!”。然后,她分别与两人各进行一轮 DH 交换:与 Alice 协商出密钥 K_AE,与 Bob 协商出密钥 K_BE。从此,Alice 发给 Bob 的密文被伊芙用 K_AE 解密、阅读、再重新加密后传给 Bob,Bob 还毫无察觉。这就是“中间人攻击”(MITM)。

这个攻击之所以能成功,根本原因在于 DH 协议本身“只协商密钥、不认证身份”。Alice 收到一个公开值 A,她只知道“有人”声称是 Bob 发来的,却没有办法验证对方真的是 Bob。就像咖啡馆里,一个陌生人坐到 Bob 的椅子上大谈蓝色色浆——Alice 误以为他就是 Bob。DH 交换的数学再完美,也解决不了“对方到底是谁”这个问题。安全通信需要“身份认证”来补齐这块短板。

现代密码学用数字签名和证书体系来补上漏洞:服务器用自己的私钥对临时公钥进行签名,客户端用 CA 证书里的公钥验证签名,确保“我手里的公开值确实来自声称的那个人”。这就是从“裸 DH”走向“认证密钥交换”的关键一步。在 TLS 里,常见做法是 ECDHE_RSA:ECDHE 负责协商会话密钥(临时、前向安全),RSA 签名负责认证服务器身份。两者结合,才算完整。

要识破这个冒名顶替者,光靠数学不行,得引入“身份徽章”——数字签名和证书。就像咖啡馆里真正的鲍勃会掏出带有官方钢印的会员证,而伊芙掏不出任何钢印。认证与密钥交换必须双剑合璧。

Alice↔ KAE Eve↔ KBE Bob,Alice 以为在跟 Bob 说话,Bob 以为在跟 Alice 说话\displaystyle \text{Alice} \xleftrightarrow{\,K_{AE}\,} \text{Eve} \xleftrightarrow{\,K_{BE}\,} \text{Bob}, \quad \text{Alice 以为在跟 Bob 说话,Bob 以为在跟 Alice 说话}
中间人攻击示意图:Eve 与 Alice、Bob 分别建立各自独立的密钥。她坐在正中间,解密并转发所有消息,两侧的通信者还浑然不觉。
  1. 双面扮演Eve 对 Alice 谎称自己是 Bob,对 Bob 谎称自己是 Alice,分别完成两轮 DH。
    Eve→Alice: “我是 Bob”,Eve→Bob: “我是 Alice”\displaystyle \text{Eve} \to \text{Alice: “我是 Bob”}, \quad \text{Eve} \to \text{Bob: “我是 Alice”}
  2. 两把独立的密钥Eve 与 Alice 共享 K_AE,与 Bob 共享 K_BE,两侧消息她全都能解密。
    KAE=ga⋅e mod p,KBE=gb⋅e mod p\displaystyle K_{AE} = g^{a \cdot e} \bmod p, \quad K_{BE} = g^{b \cdot e} \bmod p
  3. 默默转发Eve 用 K_AE 解密 Alice 的密文,读懂后用 K_BE 重新加密转发给 Bob,全程不被察觉。
    C′=EKBE(DKAE(C))\displaystyle C' = E_{K_{BE}}\left(D_{K_{AE}}(C)\right)

前向安全性

假设伊芙虽然没能实时插队,但她是个耐心的间谍:她把过去几个月来所有公开的 A、B 值都录制下来,等待着破解的曙光。终于有一天,爱丽丝和鲍勃的“长期秘密色浆配方”——比如他们长期使用的那个私钥——因为电脑中毒或内部泄露被伊芙拿到了。伊芙兴奋地回头去解那些老录音……结果却发现,过去所有会话的密钥她依然算不出来!这正是“前向安全性”(Forward Secrecy)的威力。

要理解它,先要区分两类密钥。长期密钥(Long-term Key)是服务器证书里那把固定的私钥,用来认证身份,可以用很多年;会话密钥(Session Key)是每次通信时临时协商出来的,负责加密这一次的通信数据。如果 DH 交换每次都使用固定的长期私钥(静态 DH),那么长期私钥一旦泄露,攻击者就可以倒推出历史上所有会话密钥——所有录音全部曝光,就像用同一口锅调颜料,锅的配方泄露了,过去每杯颜料都能被复制。

解决之道是“临时密钥”机制,也就是 DHE / ECDHE 中的字母 E(Ephemeral,短暂的):每次会话开始时,双方都现场随机生成全新的临时私钥 a_t、b_t,算出临时公开值交换,协商出本次独有的会话密钥;会话结束,临时私钥立即销毁,不写入任何存储介质。这样即使将来长期私钥泄露,攻击者最多只能验证“这个签名确实是你发的”,却无法解密过去任何一段会话数据——因为过去那次会话用的临时私钥早已灰飞烟灭。

前向安全性就像每一次对话都换一个崭新的一次性纸杯,说完即扔。就算未来的某天有人偷到了你的永久配方,他也无法还原那些早已被冲进下水道的旧纸杯里的秘密。正因如此,现代 TLS 握手全面拥抱 ECDHE,让“往事不可窥”成为网络通信的默认承诺。

Ksession=gat bt mod p,其中 at,bt 每次会话随机生成、用毕即焚\displaystyle K_{\text{session}} = g^{a_t\, b_t} \bmod p, \quad \text{其中 } a_t, b_t \text{ 每次会话随机生成、用毕即焚}
完美前向保密(PFS):会话密钥由一次性临时私钥 a_t、b_t 推导。会话结束即销毁临时私钥,长期私钥泄露也无法恢复历史会话密钥。
  1. 两类密钥分离长期密钥用于身份认证,会话密钥用于数据加密——两者职责分离,就像“会员证”和“一次性颜料杯”是两件东西。
    Klong (认证用),Ksession (加密用)\displaystyle K_{\text{long}} \ (\text{认证用}), \quad K_{\text{session}} \ (\text{加密用})
  2. 临时私钥DHE/ECDHE 中,a_t、b_t 由随机数生成器现场产生,不用长期私钥直接参与密钥协商。
    at←随机{1,…,p−2},bt←随机{1,…,p−2}\displaystyle a_t \xleftarrow{\text{随机}} \{1, \dots, p-2\}, \quad b_t \xleftarrow{\text{随机}} \{1, \dots, p-2\}
  3. 会话密钥推导与销毁双方算出共享密钥后,临时私钥从内存中抹除;长期密钥泄露也无法逆向,因为推导会话密钥所需的临时私钥已不存在。
    Ksession=gatbt mod p,随后 at,bt→销毁\displaystyle K_{\text{session}} = g^{a_t b_t} \bmod p, \quad \text{随后 } a_t, b_t \to \text{销毁}

TLS密钥协商

你在浏览器地址栏里打下“https://”,世界各地的服务器便借助密钥交换协议,在你和网站之间建立一条加密通道。这个过程就是 TLS 协议的“心脏跳动”——TLS 握手。它几乎把前几节的所有概念全部串了起来:密码套件(Cipher Suite)选哪种“颜料配方”、ECDHE 协商会话密钥、证书验证服务器身份、HKDF 派生最终密钥……一个安全连接,就是一场精心编排的密钥协商交响乐。

TLS 握手的核心流程(以 TLS 1.2 的 ECDHE_RSA 套件为例):客户端发送 ClientHello(支持的密码套件列表、随机数 r_c);服务器回复 ServerHello(选定的套件、随机数 r_s)、证书(含公钥)、ServerKeyExchange(携带 ECDHE 的临时公钥,并用证书私钥签名);客户端验证证书和签名后,自己生成 ECDHE 的临时公钥,发出 ClientKeyExchange;随后双方各自由临时公钥算出 ECDHE 共享秘密,再结合两个随机数经过密钥派生函数(KDF)得到主密钥与各方向用的会话密钥,最后互发 Finished 确认。短短几个来回,一把双方独有、窃听者与中间人都拿不到的密钥便就位了。

TLS 1.3 在 2018 年做了两件推动前向安全普及的大事:一是彻底删除了 RSA 密钥交换和静态 DH 等不支持前向安全的旧套件,只保留 (EC)DHE;二是将握手压缩到 1-RTT(一次往返),后量子时代的混合密钥交换也已陆续铺开。在工程实践中,还有几个铁律:优先配置 TLS 1.3 与 TLS_ECDHE_* 套件、禁用 TLS 1.0/1.1 和 export 级弱套件、为证书选择强签名算法、定期轮换并销毁旧私钥。密钥交换协议的每一个细节,最终都体现在你每一次安全访问的毫秒级握手之中。

从 Diffie-Hellman 的一杯公开颜料开始,经过近半个世纪的演进,密钥交换已经嵌入了每一次 HTTPS 请求。爱丽丝和鲍勃的咖啡馆,其实就是你每天访问的网站和浏览器——而那杯“神秘紫”,正在为数十亿人的数字生活默默护航。

ClientHello→套件,rcServerHello→证书,ECDHEsClientKeyExchange→ECDHEcKms=HKDF(gatbt mod p, rc, rs)\displaystyle \text{ClientHello} \xrightarrow{\text{套件}, r_c} \text{ServerHello} \xrightarrow{\text{证书}, \text{ECDHE}_s} \text{ClientKeyExchange} \xrightarrow{\text{ECDHE}_c} K_{\text{ms}} = \text{HKDF}\left(g^{a_t b_t} \bmod p,\ r_c,\ r_s\right)
TLS 握手流程:双方交换随机数和 ECDHE 临时公钥后,由共享秘密 g^{a_t b_t} 搭配随机数 r_c、r_s,经 HKDF 派生主密钥和会话密钥。整个过程既认证了身份,又保证了前向安全。
  1. Hello 协商ClientHello 和 ServerHello 交换随机数、确定密码套件,相当于敲定“颜料配方”。
    ClientHello(rc,套件)↔ServerHello(rs,选定套件)\displaystyle \text{ClientHello}(r_c, \text{套件}) \leftrightarrow \text{ServerHello}(r_s, \text{选定套件})
  2. 证书与签名认证服务器将证书与 ECDHE 临时公钥一并发出,私钥签名保证“此公钥确实属于本服务器”。
    CertSKs,SigSKs(ECDHEs)\displaystyle \text{Cert}_{SK_s}, \quad \text{Sig}_{SK_s}(\text{ECDHE}_s)
  3. ECDHE 交换客户端验证后发回自己的临时公钥,双方得到共享秘密 g^{a_t b_t}。
    ECDHEc→Server,shared=gatbt mod p\displaystyle \text{ECDHE}_c \to \text{Server}, \quad \text{shared} = g^{a_t b_t} \bmod p
  4. KDF 派生共享秘密加上两个随机数,进入 HKDF 派生主密钥 ms 和会话密钥;Finished 消息校验握手完整性。
    Kms=HKDF(shared,rc,rs)\displaystyle K_{\text{ms}} = \text{HKDF}(\text{shared}, r_c, r_s)

AI 应用场景

HTTPS 网页安全(网银与电商)

阶段:网络传输加密与安全信道建立

解决的问题:HTTP 明文传输时,路由器、运营商甚至 Wi-Fi 旁的攻击者都能截获密码、Cookie 和交易数据。TLS 通过 ECDHE 密钥交换为每次连接协商独立会话密钥,破解成本极高。传统方案无法在“不可信网络”上直接传递密钥,而密钥交换让密钥在公开信道上“就地生成”。

具体案例:用户登录网上银行时,浏览器与银行服务器自动执行 TLS 1.3 握手,使用 TLS_AES_256_GCM_SHA384 套件、X25519 曲线协商密钥;一旦会话结束,临时私钥即被销毁,即使后续服务器私钥泄露,也无法解密历史流量。

SSH 远程登录与 Git 传输

阶段:远程访问控制与数据保密

解决的问题:传统 Telnet 明文传密码,攻击者可轻易盗取凭据。SSH 使用 ECDH 协商会话密钥,并用服务器公钥指纹(首次连接提示)与 known_hosts 机制防止中间人。你在终端里执行 git push 到 GitHub,底层就是 SSH 的密钥协商在保护代码安全。

具体案例:开发者用 ssh-keygen 生成密钥对,执行 ssh git@github.com 时,客户端与 GitHub 服务器通过 Curve25519 ECDH 协商出一次性会话密钥,之后所有 git 数据传输都被加密。首次连接时终端显示的指纹,正是防止“咖啡馆插队者”的身份徽章。

端到端加密即时通讯

阶段:消息端到端保密与身份认证

解决的问题:微信、Signal 等通讯软件的服务器本身“不可信”,绝不能让它看到聊天内容。Signal 协议采用 X3DH(扩展三方 DH)协议:双方利用各自长期公钥 + 一次性预密钥,在异步环境下完成多轮 DH 计算,协商出只有双方持有的会话密钥。

具体案例:Alice 给 Bob 发第一条消息时,客户端自动拉取 Bob 服务器上的长期公钥和一次性预密钥,执行三路 DH(Alice 的长期 × Bob 的长期、Alice 的临时 × Bob 的长期、Alice 的长期 × Bob 的临时),组合派生会话密钥;Bob 离线也能事后解密。这保证了即使服务器被入侵,对话内容依然保密。

Python 演示

python61 行
import random  def is_prime(n):    if n < 2:        return False    for i in range(2, int(n ** 0.5) + 1):        if n % i == 0:            return False    return True  def find_primitive_root(p):    """返回素数 p 的最小原根 g"""    if p == 2:        return 1    phi = p - 1    factors = []    n = phi    i = 2    while i * i <= n:        if n % i == 0:            factors.append(i)            while n % i == 0:                n //= i        i += 1    if n > 1:        factors.append(n)    for g in range(2, p):        ok = True        for f in factors:            if pow(g, phi // f, p) == 1:                ok = False                break        if ok:            return g    return None  def dh_shared_key(p, g, a, b):    """模拟公开信道上的 Diffie-Hellman 密钥交换"""    A = pow(g, a, p)    B = pow(g, b, p)    k_alice = pow(B, a, p)    k_bob = pow(A, b, p)    assert k_alice == k_bob, "两个密钥不一致!"    return A, B, k_alice  if __name__ == "__main__":    p = 104729  # 小型安全素数,仅供演示    g = find_primitive_root(p)    a = random.randint(2, p - 2)    b = random.randint(2, p - 2)     A, B, K = dh_shared_key(p, g, a, b)    print("Open params: p = {}, g = {}".format(p, g))    print("Alice secret a = {}, Bob secret b = {}".format(a, b))    print("Public exchange: A = {}, B = {}".format(A, B))    print("Shared key: K = {}".format(K))    print("Alice and Bob just mixed the same secret color in public!")
目的:完整实现 Diffie-Hellman 密钥交换,演示双方如何在公开信道上协商出共享密钥,并验证密钥一致性。
  1. A = pow(g, a, p)用 Python 内置的快速模幂函数计算公开值 A = gᵃ mod p,这是 DH 的正向运算,毫秒级完成。
  2. B = pow(g, b, p)Bob 同样计算自己的公开值 B,A 和 B 将被放在“公开信道”上传送。
  3. k_alice = pow(B, a, p)Alice 用 Bob 的公开值和自己的秘密数算出共享密钥,相当于在鲍勃的颜料杯里加自己的秘密色浆。
  4. assert k_alice == k_bob断言双方的密钥必须一致,验证指数交换律 g^(ab) 确实成立。
  5. find_primitive_root(p)寻找模 p 的原根 g,保证公开值的周期足够大,否则容易被攻击者缩小搜索空间。
  6. p = 104729选择素数 p;演示用小素数便于观察,生产环境必须用 2048 位以上的安全素数。

常见误区

误区我以为 Diffie-Hellman 已经很安全了,它交换的密钥别人算不出来,所以中间人攻击对它肯定无效。

正解DH 只能保证“被动的窃听者”推不出共享密钥,却无法防止“主动的中间人”插到双方中间分别协商密钥。因为 DH 中没有任何身份验证环节——你收到一个公开值,并不知道它来自真 Bob 还是冒牌的 Eve。要防 MITM,必须在 DH 之外叠加数字签名和证书认证,例如 TLS 中 ECDHE + 证书签名。

为何常见:初学密码学时,很容易把“计算上不可破解”等同于“协议全方位安全”,忽略了攻击者也会主动行动。安全协议的分析必须覆盖被动窃听、主动篡改、身份冒充等多种威胁模型。

误区我以为非对称加密就是 DH,DH 能加密消息,就像 RSA 一样。

正解DH 不是加密算法,它不加密任何消息,只协商出共享密钥。RSA 可以直接用公钥加密消息、私钥解密;DH 则让双方各自计算出相同密钥,之后再配合对称加密算法(如 AES)来加密实际数据。混淆两者会导致架构设计错误——比如试图用 DH 去加密一段消息,却不知它连一条消息都发不出去。

为何常见:教材常把 DH 和 RSA 都归入“公钥密码学”大类,初学者容易把“公钥”和“密钥协商”混为一谈。

误区我的服务器支持 DH,所以过去所有会话都是前向安全的,私钥泄露也不用担心。

正解静态 DH(使用长期密钥直接协商)没有前向安全性——长期私钥一旦泄露,历史会话密钥全部可被重建。只有 DHE/ECDHE 这种“每次会话临时生成私钥、用完即焚”的版本才有 PFS。判断标准很简单:看密码套件里有没有那个 E(Ephemeral),如 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 有 E,而 TLS_DH_RSA_WITH_AES_…… 没有 E。

为何常见:大家知道“DH≈前向安全”的结论,却不知道这个结论只对 DHE/ECDHE 成立。TLS 1.3 正是因此删光了所有静态 DH 和 RSA 密钥交换套件。

误区DH 的安全性靠大素数,我把 p 设成 4096 位不就万无一失了吗?

正解p 的大小只是必要不充分条件。p 还必须是“安全素数”(p = 2q+1 且 q 是素数),生成元 g 必须具有足够大的阶;否则即使 p 很大,也可能落入“小子群”导致秘密数被轻易算出。同时随机数生成器如果脆弱(比如重用了固定 a、b),DH 也会瞬间崩溃。安全协议从来不是“大就行”,而是每个参数都需严格论证。

为何常见:直觉上认为“位数=安全强度”,忽略了代数结构。密码学中的安全是整体性质,一个短板就足以让整个协议报废。

面试题

Q1. 在 Diffie-Hellman 密钥交换中,哪些参数必须在公开信道上传输?

单选题 · easy

  • A.秘密数 a 和 b
  • B.素数 p、生成元 g、公开值 A 和 B← 正确答案
  • C.共享密钥 K
  • D.以上全部

答案:B。DH 公开传输的是 p、g、A、B。a、b 和 K 是秘密的。就像咖啡馆里公开的是基础色浆和两杯半成品,秘密色浆从不离手。

Q2. 离散对数问题的“难”,在什么前提下成立?

单选题 · easy

  • A.模数 p 是任意整数
  • B.p 足够大的素数且参数选择安全← 正确答案
  • C.g 是任意小数字
  • D.攻击者算力为 0

答案:B。离散对数的困难性依赖 p 足够大且参数正确选择(安全素数、大阶 g)。若 p 很小,小步大步攻击几秒就能破解,与攻击者算力无关。

Q3. ECDH 相比传统 DH 的主要优势是?

单选题 · mid

  • A.能防止中间人攻击
  • B.相同安全强度下密钥更短、计算更快← 正确答案
  • C.不需要随机数
  • D.不需要素数和生成元

答案:B。ECDH 用椭圆曲线离散对数替代普通离散对数,256 位安全性约等于 3072 位 DH/RSA。但它和传统 DH 一样需要认证才能防中间人,一样需要随机数和曲线参数。

Q4. 前向安全性(PFS)的核心含义是?

单选题 · mid

  • A.加密算法永远不被破解
  • B.长期私钥泄露后,历史会话密钥仍无法被还原← 正确答案
  • C.密钥可以无限次使用
  • D.所有密钥都公开

答案:B。PFS 保证“往事不可窥”——每次会话的临时私钥用完即焚,即使长期私钥后来泄露,攻击者也解不出过去的会话内容。就像一次性纸杯里的颜料倒掉后就再也无法复原。

Q5. 中间人攻击能够得逞的根本原因是?

单选题 · mid

  • A.DH 中的大素数不够大
  • B.DH 协议本身不具备身份认证能力← 正确答案
  • C.离散对数问题太简单
  • D.共享密钥计算错误

答案:B。DH 保证的是“计算上推不出密钥”,但绝口不提“对方是谁”。没有认证的裸 DH 中,Eve 可以分别和两边做 DH,各自协商一把密钥再转发消息。补救要靠数字签名和证书。

Q6. TLS 1.3 在密钥交换方面的重大变更是?

单选题 · hard

  • A.只保留 ECDHE 系列,删除 RSA 密钥交换和静态 DH← 正确答案
  • B.引入 DES 加密
  • C.允许所有旧版密码套件
  • D.不再进行身份认证

答案:A。TLS 1.3 删除了不提供前向安全的 RSA 密钥交换和静态 DH,只保留 (EC)DHE,并将握手压缩到 1-RTT。这是对“前向安全全民化”最有力的推动。

Q7. 在 DH 协议中,公开值 A = gᵃ mod p 里的“a”是什么?

单选题 · easy

  • A.公开参数
  • B.Alice 的私密随机数← 正确答案
  • C.共享密钥
  • D.生成元

答案:B。a 是 Alice 私下随机选出的秘密指数,绝不公开。它相当于爱丽丝口袋里那滴红色秘密色浆——公开值 A 是“加了自己的秘密色浆后的半成品”,但配方本身不能让人知道。

Q8. 以下哪些措施可以帮助抵御中间人攻击?

多选题 · mid

  • A.用 CA 证书绑定服务器真实公钥← 正确答案
  • B.服务器用私钥对临时公钥签名← 正确答案
  • C.将 p 从 1024 位增大到 2048 位
  • D.客户端校对 pinned 公钥指纹← 正确答案

答案:ABD。证书体系、签名、公钥固定都是在“认证身份”,从而识破冒牌插队者。只增大 p 只能加强离散对数难度,对“冒充身份”毫无帮助——伊芙哪怕算不出密钥,照样能插队转发。

Q9. 关于 DHE/ECDHE 与静态 DH,下列说法正确的是?

多选题 · hard

  • A.DHE 每次会话随机生成临时私钥← 正确答案
  • B.静态 DH 使用长期私钥参与密钥协商← 正确答案
  • C.二者都具备前向安全性
  • D.ECDHE 中的 E 代表 Ephemeral← 正确答案

答案:ABD。DHE/ECDHE 的 E 就是 Ephemeral(临时),每次会话换新临时私钥,所以有 PFS;静态 DH 用固定长期私钥,私钥泄露会连累所有历史会话,没有 PFS。C 说二者都有前向安全是错的。

Q10. 密钥交换协议通常结合哪些技术构成完整安全信道?

多选题 · mid

  • A.对称加密(如 AES)加密实际数据← 正确答案
  • B.数字签名/证书进行身份认证← 正确答案
  • C.KDF 派生会话密钥← 正确答案
  • D.压缩算法提高效率

答案:ABC。完整链路 = 密钥交换(协商共享秘密)+ KDF(派生密钥)+ 对称加密(加密数据)+ 签名/证书(认证身份)。压缩算法与安全性无关,不参与密钥协商。