SM2 算法概述与数字签名原理
这一章我们先聊聊 SM2 是个什么东西,它的核心又是怎么跑起来的。我这些年经手的国密项目,十有八九都会碰到 SM2。说实话,刚开始接触时我也觉得它就是椭圆曲线签名那一套,后来踩过坑才明白——细节决定成败。
SM2 是国家密码管理局在 2010 年发布的椭圆曲线公钥密码算法标准(GM/T 0003-2012)。它包含了数字签名、密钥交换和公钥加密三个部分。今天我们重点讲数字签名,因为这个场景最容易出现签名失败率异常的问题。你想想看,一个支付系统如果签名失败率高,用户就收不到通知,那问题可就大了。
为什么是椭圆曲线?
SM2 基于椭圆曲线密码学(ECC),它的核心优势就是——在同样安全强度下,密钥比 RSA 短得多。我记得有个项目要求硬件芯片存储 1000 个公钥,如果用 RSA-2048,光公钥就得 256KB 空间;换成 SM2 的话,每个公钥只要 64 字节,直接省了 4 倍空间。
SM2 使用的曲线是国密局定义的 256 位素域上的椭圆曲线。它的参数是固定的,包括基点和阶。你不用去选曲线,直接用标准参数就行。我曾经见过有人自己乱改曲线参数,结果签名验签死活对不上——得不偿失。
数字签名的核心要素
先理清楚几个必须搞懂的概念:
- 私钥:一个随机生成的 256 位整数,只有签名者持有。
- 公钥:私钥乘以基点得到的椭圆曲线点,公开给所有人。
- 消息:要签名的原文,通常是交易的摘要或文档哈希。
- 签名结果:由两个整数 (r, s) 组成,r 是点横坐标,s 是私钥和随机数的混合运算结果。
为什么签名失败率会异常?说白了就是 r 或 s 中某个值不满足合法性条件,或者运算过程引入了不确定因素。后面几章我们会专门分析原因,这一章先把原理讲清楚。
SM2 签名生成流程(标准版)
标准流程其实不复杂,我画张图帮你捋一下:
流程看起来简单吧?但仔细看步骤④和⑤:
- r = e + x1 (mod n) —— 其中 e 是 SM3 哈希结果转换成整数,x1 是椭圆曲线点的横坐标。这里要求 r 不能为 0,不能大于 n(基点阶)。
- s = (1+dA)⁻¹(k - r·dA) mod n —— dA 是私钥。这里 s 也不能为 0,且需要保证签名是可恢复的。
签名验证流程
验证方拿到签名 (r, s) 和消息原文后,会做以下操作:
- 计算消息哈希 e = SM3(消息)。
- 检查 r, s 是否在 [1, n-1] 内,不满足直接拒绝。
- 计算 t = r + s (mod n),如果 t = 0 则签名无效。
- 计算椭圆曲线点 (x1', y1') = s*G + t*PA(PA 是公钥)。
- 计算 R = e + x1' (mod n),如果 R = r 则签名通过。
嗯,你可能会问:为什么验证公式和生成公式不一样?其实它们是等价的,验证只是把生成过程倒过来推了一遍。
关键参数一览
| 参数 | 含义 | 模长 | 异常风险 |
|---|---|---|---|
| n | 基点的阶 | 256 bit | 如果随机数 k >= n,签名会失败 |
| dA | 私钥 | [1, n-2] | 生成时 dA = 0 或越界,直接失败 |
| PA | 公钥点 | 两个 256 bit 坐标 | 无效地址导致验证不过 |
| e | 哈希整数 | ≤ 256 bit | 哈希截断错误会导致签名不一致 |
| (r, s) | 签名值 | 各 256 bit | r=0 或 s=0 或 s>n/2 等均不合规 |
重点记忆: 签名失败率异常往往出现在 r 或 s 恰好等于 0 的概率极低(2⁻²⁵⁶),但在算法实现中,如果随机数生成器质量差、或者模逆运算出错,小概率事件会变成大概率事故。所以后续章节我们会重点排查随机数生成和模逆计算的实现细节。
我的团队曾经踩过的一个典型问题
几年前一个金融项目,SM2 签名验签成功率一直在 99.97% 左右,也就是每 3000 笔就有一笔失败。排查了很久,后来发现是 SM3 哈希结果转整数时,字节序搞反了——大端和小端用混了。你想想看,1000 笔签名里就有三四笔因为字节序不对导致 e 值不同,r 算出来自然不对。修复后成功率直接到了 99.9999% 以上。
这类问题,光看 API 日志很难定位。我们需要一套系统的检测方法,从随机源、哈希转换、模运算到最终签名输出,逐级插入观测点。这就是这门课要做的事情。
总结一下这一章的核心
SM2 数字签名本质上是一个三步走的椭圆曲线运算:哈希、随机数生成、曲线点乘加。每一环都有可能导致签名失败率异常。我个人的习惯是,先把标准流程和参数边界背熟,然后再去对照实际代码找偏差。后面几章,我们会深入每一个环节的失败模式,给出具体的检测和修复方案。