密钥协商协议基础:Diffie-Hellman原理与SM2规范

这一章我们聊聊密钥协商的根基。说白了,就是两个互不信任的实体,怎么能在一个被偷听的信道里,安全地聊出一个只有他俩知道的秘密。

我在2018年做过一个安全通信项目,甲方要求所有会话密钥必须“一次一密”。那时我第一次真正面对密钥协商的工程实现——理论很简单,落地全是坑。

Diffie-Hellman密钥交换:核心思想

DH协议是1976年的作品,但它至今仍然是许多现代密钥协商方案的内核。它的原理其实不复杂,关键是利用了“离散对数难题”这个数学陷阱门。

我给你一个直观的理解方式。想象一下调颜色:

  • Alice和Bob约定一种公共颜料(比如黄色),这是公开的,谁都能看到。
  • Alice私下选一种颜色(比如蓝色),Bob也私下选一种颜色(比如红色)。
  • Alice把黄色和蓝色混合,得到绿色颜料,发给Bob。Bob把黄色和红色混合,得到橙色颜料,发给Alice。
  • 现在,Alice收到橙色颜料,加上自己的蓝色,能得到什么?橙色加蓝色,其实又是黄色+红色+蓝色。
  • Bob收到绿色颜料,加上自己的红色,也能得到同样的黄色+蓝色+红色。

这个过程的关键是:虽然混合后的颜料(黄色+蓝色)是公开传输的,但任何第三方都无法从“黄色+蓝色”这个混合色中分离出Alice的“蓝色”私密成分。

嗯,注意:颜色混合是可逆的吗?物理上讲几乎不可能。但在数学上,模幂运算的反向操作就是“离散对数”,这是一个公认的困难问题——只要参数选得足够大。

数学化的DH流程可以归纳成这样:

步骤 Alice Bob 公开信道可见
1. 选择公共参数 选择一个素数 p 和一个底数 g (与Alice相同) p, g
2. 生成私钥 随机选取整数 a 随机选取整数 b (不可见)
3. 计算公钥 A = g^a mod p B = g^b mod p A, B
4. 交换公钥 发送 A 给 Bob 发送 B 给 Alice A, B
5. 计算共享密钥 K = B^a mod p K = A^b mod p (不可见)

最终,Alice和Bob各自计算出的K是完全相同的。攻击者虽然截获了p、g、A、B,但要从中计算a或b,就需要求解离散对数问题——在参数量级达到256位以上时,这几乎不可能。

这是我踩过的坑:DH协议有一个经典弱点——它不提供身份认证。中间人攻击可以完美绕过它。我在项目中曾经因为没在DH交换前做好身份绑定,导致测试环境被伪造节点截获了会话密钥。从那以后,我再也不敢省略身份认证步骤。

SM2密钥协商协议:国密标准的实现

SM2密钥协商协议本质上是在椭圆曲线密码学(ECC)上实现的DH变体。它结合了中国国家密码管理局制定的SM2曲线参数和数字签名、密钥派生等机制。

为什么不用经典DH而用EC-DH呢?给你一组对比数据就清楚了:

安全强度(比特) RSA/DL模数长度(位) 椭圆曲线密钥长度(位)
80 1024 160
112 2048 224
128 3072 256
192 7680 384
256 15360 512

你看,要达到128比特安全强度,经典DH需要3072位的模数,而椭圆曲线只需要256位。这意味着更少的计算量、更短的通信负载、更低的存储开销。

SM2密钥协商规范要点

SM2密钥协商协议的定义在GMT 0003.3-2012标准中。我建议你直接看那个标准文件,非常清晰。这里我帮你拆解最核心的部分:

  1. 双方初始化:通信双方各有一对SM2密钥对(私钥dA、公钥PA;私钥dB、公钥PB),以及各自的证书。
  2. 临时密钥生成:每个参与方生成一个临时密钥对(rA, RA)和(rB, RB),其中RA = [rA]G,G是基点。
  3. 交换临时公钥:Alice发送RA给Bob,Bob发送RB给Alice。注意,这里同时需要交换对方的身份标识符(IDA和IDB)。
  4. 计算共享密钥:双方使用一个公式,结合对方的临时公钥、自己的临时私钥、对方的静态公钥、自己的静态私钥、以及双方身份标识符,计算出相同的共享密钥K。

SM2密钥交换协议的共享密钥计算公式是:

// Alice计算侧
K = KDF(xU || yU || ZA || ZB, klen)

// 其中xU, yU是椭圆曲线上的点U的坐标
U = [h * tA] (PB + [RB] * RB的协调因子部分...)

// 简化理解版本:
U_A = [dA + rA * RA_x](PB) + [rA](RB)
// 其中RA_x是RA点的x坐标,dA是Alice的静态私钥,rA是临时私钥

这里的巧思在于:最终的密钥不仅依赖于双方临时密钥的DH交换,还绑定了双方的静态长期密钥。这带来了几个安全属性:

  • 隐式认证:如果Alice的静态公钥PB是Bob的真实公钥(通过证书验证),那么只有Bob才能用自己的私钥dB完成同一计算。
  • 完美前向保密:即使长期私钥泄漏,历史会话密钥仍安全,因为每次协商都使用不同的临时密钥。
  • 密钥确认:协议最后有一个可选步骤,双方可以验证对方确实算出了相同的密钥。

核心理解:SM2密钥协商本质上是一个“两次DH”的组合——一次是用静态密钥对(长期绑定身份),一次是用临时密钥对(确保前向安全)。两者通过身份标识符ZA、ZB绑定在一起,避免中间人攻击。

我个人习惯在实现时把整个过程分为四个阶段:初始化、临时密钥生成与交换、会话密钥计算、密钥确认。每个阶段都做参数验证——输入的长度检查、点是否在曲线上、结果是否无穷远点等。这些检查看似繁琐,但能避免很多安全漏洞。

SM2密钥协商流程总览

下面这张图展示了完整的协商过程。我建议你先看整体流程,再逐个理解每个步骤中的数学运算。

SM2密钥协商完整流程 SM2密钥协商协议完整流程 Alice(发起方) ① 初始化 静态密钥对 (dA, PA)、IDA ② 生成临时密钥 随机数 rA,计算 RA = [rA]G ③ 发送数据给Bob 发送 RA || IDA 给 Bob 网络 Bob(响应方) ① 初始化 静态密钥对 (dB, PB)、IDB ② 生成临时密钥 随机数 rB,计算 RB = [rB]G ③ 发送数据给Alice 发送 RB || IDB 给 Alice 网络 ④ 各自计算共享密钥 K 结合静态密钥、临时密钥、身份标识 关键安全属性 ✓ 隐式认证:双方静态密钥绑定了身份 ✓ 完美前向保密:临时密钥每次不同 ✓ 抗中间人攻击:ZA、ZB参与计算 ✓ 密钥确认:可选步骤验证计算结果

与经典DH的对比和工程启示

维度 经典DH(整数域) SM2(椭圆曲线域)
安全基础 离散对数问题(整数域) 椭圆曲线离散对数问题
密钥长度 2048位(112位安全强度) 256位(128位安全强度)
计算开销 大整数模幂运算 椭圆曲线点乘运算
身份认证 需要额外签名或证书 内置于协议中(静态密钥对)
完美前向保密 可选实现 强制实现
密钥确认 可选步骤 标准推荐实现

我个人在项目中通常优先选择SM2而不是经典DH,原因就是它把身份认证、密钥交换和前向安全做到了一起,减少了协议组合出错的概率。

工程落地小建议:永远不要在自己的实现里写“如果对方没发什么东西,就用默认值”。SM2规范对每一步都有严格的数据格式要求,包括字节序、填充规则。我见过一个项目因为临时公钥的x坐标没有正确编码,导致两端计算出的K不同——排查了三天才发现。