SM2 签名编码格式实战:R+S、DER 与 GB/T 35275-2026 新标准深度解析

国密算法 · 2026-06-16 · 6 阅读

前言

SM2 国密算法在实际工程中最让人头疼的问题之一,不是算法本身,而是签名的编码格式。你可能遇到过这些场景:

  • gmssl 签出来的 64 字节 R+S 格式,对方系统不认,要求 DER 编码
  • 密评(密评)测评机构要求签名格式符合 GB/T 35275,但你不知道新版标准和旧版有什么区别
  • 跨系统对接时,Java 端用 BouncyCastle 签出的 DER 和 Python 端 gmssl 签出的 R+S 互相验签失败
  • 对方说"签名长度不对",你发现一个是 64 字节,一个是 71 字节
这些问题都指向同一个核心:SM2 签名的编码格式没有统一,不同系统、不同库、不同场景使用不同格式

本文从实战角度,完整覆盖:

  • 两种编码格式的本质区别:R+S(64 字节)vs DER(70-72 字节)
  • GB/T 35275-2026 新标准的变化:与 2017 版有什么区别,对工程有什么影响
  • 完整格式转换代码:R+S ⟷ DER 双向转换,含边界处理
  • 跨语言互操作:Python gmssl、Java BouncyCastle、Node.js 的格式对齐
  • 密评合规要点:密评中如何证明你的签名格式是合规的
  • 常见踩坑与排查:6 个真实场景的排查过程

一、两种格式的本质

1.1 R+S 格式(非编码格式)

SM2 签名的数学本质是两个 256 比特的整数 R 和 S。最直接的表示方式就是将它们拼接起来:

CODE
签名 = R || S
R: 32 字节(256 比特)
S: 32 字节(256 比特)
总长度: 64 字节(512 比特)

这是 GM/T 0003.2-2012(SM2 算法标准)和 GM/T 0009-2012(SM2 使用规范)中定义的"非编码格式"。gmssl 库的默认输出就是这种格式。

特点:

  • 长度固定 64 字节
  • 无额外结构开销
  • 纯二进制,不适合文本协议传输
  • 国密系统内部常用

1.2 DER 编码格式(ASN.1)

DER 编码使用 ASN.1 的 SEQUENCE 结构,将 R 和 S 包装成 TLV(Type-Length-Value)格式:

CODE
30 [总长度] 02 [R长度] [R值] 02 [S长度] [S值]
│  │        │  │       │    │  │       │
SEQUENCE   INTEGER  R    INTEGER  S

为什么长度是 70-72 字节?

因为 ASN.1 INTEGER 的编码规则要求:当整数的最高位为 1 时,前面必须补一个 0x00 字节(否则会被误认为是负数)。R 和 S 都是 256 比特的正整数,它们的值在 [1, n-1] 范围内均匀分布,约 50% 的概率最高位为 1。

场景R 补位S 补位总长度
R < 0x80, S < 0x80不需要不需要70 字节
R ≥ 0x80, S < 0x80需要不需要71 字节
R < 0x80, S ≥ 0x80不需要需要71 字节
R ≥ 0x80, S ≥ 0x80需要需要72 字节
特点:
  • 长度可变(70-72 字节)
  • 自描述,可逐字节解析
  • 与 X.509 证书、PKCS#7 等标准兼容
  • 跨系统互操作时推荐使用
  • 国际标准(如 TLS 证书中的 SM2 签名)要求 DER 编码

1.3 格式对比

维度R+S 格式DER 编码
长度固定 64 字节70-72 字节
标准来源GM/T 0003.2 / GM/T 0009GB/T 35275 / ASN.1
可读性不可直接解析可逐字节解析
文本传输需要 Base64/Hex需要 Base64/Hex
跨系统国密内部兼容国际通用
密评推荐内部使用对外交互
确定性完全确定取决于 R/S 值

二、GB/T 35275-2026 新标准解读

2.1 标准演进

GB/T 35275 是 SM2 签名消息格式的国家标准,对应国密标准的 GM/T 0009《SM2 密码算法使用规范》。

版本发布年份标准名称状态
GB/T 35275-20172017信息安全技术 SM2密码算法加密签名消息语法规范即将被替代
GB/T 35275-20262026网络安全技术 SM2密码算法加密签名消息格式即将实施(2026-12-01)
2026 年 5 月 25 日发布,2026 年 12 月 1 日起正式实施。由格尔软件牵头修订,全国网络安全标准化技术委员会归口。

2.2 主要变化

名称变化:从"信息安全技术"改为"网络安全技术",与现行网络安全标准体系对齐。

内容变化(基于标准制定趋势和参与单位信息):

  • 编码格式推荐:新标准更加强调 DER 编码在跨系统互操作中的重要性,建议对外交互统一使用 DER 编码
  • 与 GM/T 系列协调:与 GM/T 0003.2、GM/T 0009 等国密标准的最新修订版保持技术一致
  • OID 标识更新:可能引入新的 OID 标识以区分新旧格式
  • 测试向量更新:提供更完整的测试用例,特别是边界值场景

2.3 对工程的影响

对于正在做密评合规或国密改造的团队:

  • 新系统建议直接采用 DER 编码:避免后续格式转换的额外工作
  • 存量系统如果已经是 R+S 格式:不需要强制转换,但在对外接口处做好格式适配
  • 密评测评:测评机构会关注签名格式是否符合标准要求,DER 编码更容易通过

三、完整代码实战

3.1 环境准备

BASH
# 安装 gmssl(国密算法库)
pip install gmssl

# 如果需要 SM3 哈希,cryptography 44.x 也支持
pip install cryptography>=44.0

环境要求:

  • Python 3.8+
  • gmssl 3.x(本文基于 gmssl 3.2.x 验证)

3.2 密钥生成

SM2 密钥对包括一个 256 比特的私钥 d 和一个 512 比特的公钥 P = d × G:

3.3 R+S 格式签名与验签

⚠️ 注意:gmssl 的 sign() 方法接收的 data 参数是 SM3 哈希的字节,不是原始消息。这是很多初学者的第一个坑。

3.4 DER 编码签名与验签

3.5 R+S ⟷ DER 双向转换

在实际工程中,最常见的场景是在两种格式之间转换:

3.6 DER 编码长度分析工具

3.7 加密密文的格式选择

SM2 加密也有两种格式,由 mode 参数控制:

四、踩坑实录

坑 1:sign/verify 的 data 参数是哈希,不是原始消息

现象:签名时传入原始消息,验签也传原始消息,结果返回 False。

原因:gmssl 的 sign()verify() 方法的 data 参数要求是 SM3 哈希值(32 字节),不是原始消息。这是 gmssl 库的设计决策,它把哈希计算交给了调用者。

PYTHON
# ❌ 错误
sig = csm2.sign(msg, K)        # msg 是原始消息
result = csm2.verify(sig, msg)  # 也传原始消息 → False

# ✅ 正确
msg_hash_bytes = bytes.fromhex(sm3_hash([i for i in msg]))
sig = csm2.sign(msg_hash_bytes, K)
result = csm2.verify(sig, msg_hash_bytes)  # True

坑 2:DER 和 R+S 格式混用导致验签失败

现象:用 DER 格式签名,但用非 DER 方式验签,返回 False。

原因:DER 编码的签名包含 ASN.1 结构信息(SEQUENCE、INTEGER 标签和长度),如果验签方按 R+S 格式解析,会把标签字节当作 R 的前几个字节,导致 R 和 S 的值完全错误。

PYTHON
# ❌ 混用
sig_der = csm2_der.sign(msg_hash_bytes, K)  # DER 编码
result = csm2_rs.verify(sig_der, msg_hash_bytes)  # 按 R+S 解析 → False

# ✅ 统一格式
result = csm2_der.verify(sig_der, msg_hash_bytes)  # True

坑 3:随机数 K 不可重复使用

现象:两次签名使用相同的随机数 K,攻击者可以直接计算出私钥。

原因:SM2 签名算法中,如果 K 重复,则 R 相同,攻击者可以通过两个签名的 S 值联立方程解出私钥 d。

PYTHON
# ❌ 危险!不要这样做
K_fixed = func.random_hex(64)
sig1 = csm2.sign(hash1, K_fixed)
sig2 = csm2.sign(hash2, K_fixed)  # K 重复 → 私钥泄露

# ✅ 每次签名生成新的随机数
sig1 = csm2.sign(hash1, func.random_hex(64))
sig2 = csm2.sign(hash2, func.random_hex(64))

gmssl 的 sign_with_sm3() 方法会自动生成安全的随机数,推荐使用。

坑 4:公钥的 04 前缀

现象:从证书中提取的公钥带有 04 前缀(非压缩格式标识),传给 gmssl 时是否需要去掉?

结论:gmssl 的 CryptSM2 构造函数会自动处理 04 前缀,但建议统一去掉,避免混淆。

PYTHON
# 两种方式都可以,gmssl 自动处理
pub_with_04 = "04" + public_key_hex
csm2_a = sm2.CryptSM2(public_key=pub_with_04, private_key=private_key_hex)

pub_without_04 = public_key_hex
csm2_b = sm2.CryptSM2(public_key=pub_without_04, private_key=private_key_hex)

# 内部存储一致
print(csm2_a.public_key == csm2_b.public_key)  # True

坑 5:密评中的编码格式合规

现象:密评测评机构要求证明签名格式符合 GB/T 35275。

应对

  • 对外接口统一使用 DER 编码:密评更容易认可 DER 格式
  • 保留测试向量:用 GM/T 0009 附录中的测试向量验证你的实现
  • 代码中明确标注:在代码注释中引用标准编号
  • 提供转换能力:证明系统能在两种格式间正确转换

坑 6:cryptography 库的 SM2 限制

现象:尝试用 cryptography 库的 ec 模块生成 SM2 密钥,报错或结果不正确。

原因cryptography 库(截至 44.x)不支持 SM2 曲线的密钥生成和签名操作。cryptography 支持的是 NIST 曲线(P-256、P-384、P-521)和 SECG 曲线(secp256k1),但不包括 SM2 的 sm2p256v1 曲线。

cryptography 44.x 新增了对 SM3 哈希的支持,但不包括 SM2 签名。

结论:SM2 操作必须用 gmssl(或 Java BouncyCastle),cryptography 只能配合做 SM3 哈希。两套库的密钥格式不兼容,不能混用。

五、跨系统互操作指南

5.1 Python ⟷ Java

Java 的 BouncyCastle 库默认使用 DER 编码。Python gmssl 默认使用 R+S。对接时在边界处转换:

5.2 Python ⟷ 硬件加密机

大多数硬件加密机(HSM/加密卡)返回的是 R+S 格式签名,因为硬件实现通常直接输出数学结果,不包含 ASN.1 编码逻辑。

PYTHON
# 硬件加密机返回 R+S 格式
hw_signature_rs = call_hardware_sign(msg_hash_hex)  # 64 字节

# 转换为 DER 格式供上层应用使用
hw_signature_der = rs_to_der(hw_signature_rs[:64], hw_signature_rs[64:])

5.3 与 TLS/HTTPS 的关联

在国密 TLS(TLCP,GM/T 0024)中,证书链中的 SM2 签名是 DER 编码的(因为 X.509 证书格式要求)。握手过程中的 CertificateVerify 消息中的签名也必须是 DER 编码。

如果你在部署国密 HTTPS(参见本系列文章《SM2 国密算法实战:从密钥生成到 TLS 完整部署》),证书签名格式由 GmSSL/OpenSSL 自动处理,不需要手动转换。

六、实用工具函数

6.1 完整的签名格式工具类

七、总结

关键要点

  • 两种格式本质相同:都是 R 和 S 两个 256 比特整数,区别仅在于编码方式
  • R+S 是国密默认:gmssl、硬件加密机、国密内部系统多用 R+S
  • DER 是跨系统标准:X.509、TLS、国际互操作要求 DER
  • 转换很简单:就是 ASN.1 的 SEQUENCE + INTEGER 包装,20 行代码搞定
  • GB/T 35275-2026 强化了 DER 的重要性:新系统建议直接采用 DER
  • 密评合规:对外接口用 DER,内部可以用 R+S
  • gmssl vs cryptography:SM2 操作必须用 gmssl,cryptography 只能做 SM3

选型建议

场景推荐格式理由
国密 HTTPS 证书DERX.509 标准要求
内部系统间调用R+S长度短,效率高
与外部系统对接DER兼容性最好
密评测评对外接口DER符合标准要求
硬件加密机输出R+S硬件直接输出数学结果
JWT/Token 签名DER与 JWS 标准兼容

下一步阅读

  • 如需了解 SM2 在 TLS 中的完整部署流程,请参阅 GM/T 0024-2014《SSL VPN 技术规范》和 RFC 8998《SM2 算法在 TLS 1.3 中的应用》
  • 如需了解 SM2 与 ECDSA/EdDSA 的方案对比,可参考 NIST FIPS 186-5《Digital Signature Standard》和 RFC 6979《Deterministic Usage of DSA and ECDSA》
  • 如需了解国密 HTTPS 部署中的常见问题,请参阅 GB/T 38636-2020《信息安全技术 网络安全等级保护基本要求》中的密码学要求

参考来源