SM2 椭圆曲线公钥密码算法
SM2 椭圆曲线公钥密码算法
SM2 同时覆盖签名、公钥加密和密钥交换。GMKit 的 Java 与 TypeScript 包固定使用标准 SM2 曲线;算法名称相同并不代表身份、签名格式、密文排列或长度单位可以省略。
协议必须固定的字段
私钥是 1 到曲线阶减 1 的标量,通常表示为 32 字节或 64 个 Hex 字符。非压缩公钥为 04 || x || y,压缩公钥为 02/03 || x。公钥压缩只改变点编码,不改变密钥本身。
签名中的 Z 不能省略
标准 SM2 签名先计算:
Z = SM3(ENTL || ID || a || b || xG || yG || xA || yA)
e = SM3(Z || M)ID、消息字节和公钥都会进入最终摘要。默认身份只是兼容值,不应代替业务协议中的身份定义。
TypeScript 的 skipZComputation,以及 Java 的同名 Builder 选项、signWithoutZ、verifyWithoutZ、computeEWithoutZ 均为弃用的旧协议兼容入口。Bouncy Castle 1.83 的 SM2Signer 没有公开的跳过 Z 选项;no-Z 签名不是性能优化,也不是“另一种标准模式”。
当前版本中,省略 userId 或传空字符串都会回落到默认身份。Java GM_2023_USER_ID 的值为空字符串,因此会被 Builder 同样映射到旧默认身份,不能表示独立的空 ID,现已标记弃用。
加密载荷
SM2 加密含随机临时标量,相同公钥和明文每次应产生不同密文。接收方至少需要知道密文排列和外层编码;原始密文还必须包含合法 C1 点、32 字节 C3 和非空 C2。
C3 不匹配、点编码无效、DER 非最短编码或尾随数据都属于失败,不能返回部分明文。SM2 不适合直接加密大文件;常见做法是用认证加密处理业务载荷,再由 SM2 保护随机会话密钥。
密钥交换不是普通 ECDH
双方都需要静态密钥、临时密钥、身份和角色,并应校验确认标签。派生长度的单位是两端最容易写错的差异:
验收重点
- 标准 GMKit 签名可由 BC 1.83 验证,BC 标准签名也可由 GMKit 验证。
- 同一消息使用不同身份时必须验签失败。
- no-Z 签名只能在 GMKit 同配置旧路径下成功,BC 标准验签必须失败。
- 修改消息、签名、密文 C3 或公钥点后必须失败。
- 固定向量用于验证确定性字段;随机签名和随机密文只检查互操作与篡改拒绝,不比较完整字面值。