4. SM2数字签名算法:签名生成、验证与ECDSA对比

数字签名,说白了就是给一段数据盖个「电子印章」。你签了名,别人就能确认这数据是你发的,而且中途没人动过手脚。SM2的签名算法和ECDSA(椭圆曲线数字签名算法)长得很像,但细节上差别不小。我当年第一次接触SM2时,心想这不就是ECDSA换了个曲线吗?结果踩了几个坑才明白——差之毫厘,谬以千里。

4.1 签名生成流程

SM2签名生成,核心是「先算杂凑,再算签名」。具体分五步走:

  1. 预处理消息:把待签名消息M和签名者身份标识ZA拼在一起,计算SM3杂凑值e = SM3(ZA || M)。ZA包含了签名者的公钥和椭圆曲线参数。
  2. 生成随机数:产生一个随机数k,范围在[1, n-2]之间。n是椭圆曲线的阶。
  3. 计算椭圆曲线点:计算点(x1, y1) = k * G,G是基点。取x1的数值部分。
  4. 计算签名分量r:r = (e + x1) mod n。如果r等于0或r + k等于n,就要重新选k。
  5. 计算签名分量s:s = ((1 + dA)^(-1) * (k - r * dA)) mod n。dA是签名者的私钥。如果s等于0,也要重新选k。

最终签名值是(r, s),两个大整数。嗯,这里要注意:SM2的r和e是加法关系,不是乘法关系。这和ECDSA完全不同。

关键点:SM2签名公式 s = ((1 + dA)^(-1) * (k - r * dA)) mod n,其中(1 + dA)^(-1)是(1 + dA)模n的逆元。这个结构保证了签名验证时能正确恢复出公钥信息。

4.2 签名验证流程

验证签名时,接收方拿到消息M和签名(r, s),按以下步骤检查:

  1. 检查范围:确认r和s都在[1, n-1]范围内。不在就直接拒绝。
  2. 计算杂凑值:同样计算e = SM3(ZA || M)。
  3. 计算中间量t:t = (r + s) mod n。如果t等于0,验证失败。
  4. 计算椭圆曲线点:计算点(x1', y1') = s * G + t * PA。PA是签名者的公钥。
  5. 验证r:计算R = (e + x1') mod n。如果R等于r,验证通过;否则失败。

我刚开始做SM2验证时,总搞不清为什么验证公式是s * G + t * PA而不是s * G - r * PA。后来推导了一遍才明白——SM2的签名公式设计就是为了让验证时能消掉私钥dA,只留下公钥PA。这个设计很巧妙。

我的经验:验证时最容易出错的是t的计算。t = (r + s) mod n,不是r + s直接相加。如果r + s超过n,必须取模。我曾经在嵌入式平台上踩过这个坑——硬件除法器精度不够,导致t算错了,签名死活验证不过。

4.3 SM2签名与ECDSA签名的区别

ECDSA是国际标准,SM2是中国标准。两者都基于椭圆曲线,但签名公式和验证公式的结构完全不同。我整理了一张对比表:

对比项 SM2 ECDSA
杂凑算法 SM3(256位) SHA-1/SHA-256等
签名公式 s = ((1+dA)^(-1) * (k - r*dA)) mod n s = (k^(-1) * (e + r*dA)) mod n
r的计算 r = (e + x1) mod n r = x1 mod n
验证公式 R = (e + x1') mod n,比较R与r 计算u1 = e*s^(-1), u2 = r*s^(-1),验证点是否匹配
随机数重用风险 泄露私钥 泄露私钥
签名长度 64字节(r和s各32字节) 64字节(r和s各32字节,256位曲线)

你看,最核心的区别在于r的计算方式。SM2把杂凑值e直接加进了r,而ECDSA的r只依赖随机点x1。这意味着SM2的签名值r和消息内容直接绑定,安全性更高。但代价是——SM2签名不能像ECDSA那样做「签名恢复公钥」。ECDSA可以从签名和消息中恢复出公钥,SM2不行。

避坑指南:我曾经在对接一个国际系统时,对方要求用ECDSA的签名恢复公钥功能。我一开始想用SM2替代,结果发现SM2不支持这个特性。最后只能保留ECDSA做公钥恢复,SM2做普通签名。所以选型时一定要搞清楚业务场景是否需要签名恢复。

4.4 签名流程对比图

下面这张SVG图把SM2和ECDSA的签名流程放在一起对比。你可以直观看到两者的差异:

SM2 vs ECDSA 签名流程对比 SM2 签名流程 ① 计算 e = SM3(ZA || M) ② 生成随机数 k ∈ [1, n-2] ③ 计算 (x1, y1) = k * G ④ r = (e + x1) mod n ⑤ s = ((1+dA)^(-1) * (k - r*dA)) mod n 输出 (r, s) ECDSA 签名流程 ① 计算 e = SHA256(M) ② 生成随机数 k ∈ [1, n-1] ③ 计算 (x1, y1) = k * G ④ r = x1 mod n ⑤ s = (k^(-1) * (e + r*dA)) mod n 输出 (r, s) r 计算方式不同 s 公式结构不同

4.5 实际项目中的选择建议

我个人习惯这样选:

  • 国内合规场景:必须用SM2。等保2.0、密评、政务系统,没得选。
  • 国际互通场景:用ECDSA。SM2在国外支持度低,对接成本高。
  • 混合场景:同时支持两种签名算法。我做过一个项目,服务端同时支持SM2和ECDSA,客户端根据配置选择。这样既满足合规,又保证兼容。

我的经验:SM2签名验证的性能和ECDSA差不多,都是椭圆曲线点乘运算。但SM2的杂凑计算(SM3)比SHA-256略慢。如果你在高并发场景下做签名验证,建议用硬件加速卡。我在一个金融项目中,用软件实现SM2签名只能做到每秒2000次,换成硬件加速后直接飙到每秒5万次。

好了,SM2数字签名算法的核心内容就这些。你想想看,签名生成和验证的公式其实是对称的——生成时用私钥,验证时用公钥。理解了这一点,SM2签名就没那么神秘了。