SM2 签名编码格式实战:R+S、DER 与 GB/T 35275-2026 新标准深度解析
前言
SM2 国密算法在实际工程中最让人头疼的问题之一,不是算法本身,而是签名的编码格式。你可能遇到过这些场景:
- 用
gmssl签出来的 64 字节 R+S 格式,对方系统不认,要求 DER 编码 - 密评(密评)测评机构要求签名格式符合 GB/T 35275,但你不知道新版标准和旧版有什么区别
- 跨系统对接时,Java 端用 BouncyCastle 签出的 DER 和 Python 端 gmssl 签出的 R+S 互相验签失败
- 对方说"签名长度不对",你发现一个是 64 字节,一个是 71 字节
本文从实战角度,完整覆盖:
- 两种编码格式的本质区别: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。最直接的表示方式就是将它们拼接起来:
签名 = 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)格式:
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 0009 | GB/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-2017 | 2017 | 信息安全技术 SM2密码算法加密签名消息语法规范 | 即将被替代 |
| GB/T 35275-2026 | 2026 | 网络安全技术 SM2密码算法加密签名消息格式 | 即将实施(2026-12-01) |
2.2 主要变化
名称变化:从"信息安全技术"改为"网络安全技术",与现行网络安全标准体系对齐。
内容变化(基于标准制定趋势和参与单位信息):
- 编码格式推荐:新标准更加强调 DER 编码在跨系统互操作中的重要性,建议对外交互统一使用 DER 编码
- 与 GM/T 系列协调:与 GM/T 0003.2、GM/T 0009 等国密标准的最新修订版保持技术一致
- OID 标识更新:可能引入新的 OID 标识以区分新旧格式
- 测试向量更新:提供更完整的测试用例,特别是边界值场景
2.3 对工程的影响
对于正在做密评合规或国密改造的团队:
- 新系统建议直接采用 DER 编码:避免后续格式转换的额外工作
- 存量系统如果已经是 R+S 格式:不需要强制转换,但在对外接口处做好格式适配
- 密评测评:测评机构会关注签名格式是否符合标准要求,DER 编码更容易通过
三、完整代码实战
3.1 环境准备
# 安装 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:
import binascii
from gmssl import sm2, func
from gmssl.sm3 import sm3_hash
# 生成私钥(32 字节,64 个十六进制字符)
private_key_hex = func.random_hex(64)
print(f"私钥: {private_key_hex}")
# 计算公钥 P = d × G
# gmssl 的 CryptSM2 内部实现了椭圆曲线点乘
ecc_table = sm2.default_ecc_table
# 创建临时对象用于计算公钥
temp = object.__new__(sm2.CryptSM2)
temp.private_key = private_key_hex
temp.para_len = len(ecc_table['n'])
temp.ecc_table = ecc_table
temp.ecc_a3 = (int(ecc_table['a'], 16) + 3) % int(ecc_table['p'], 16)
temp.public_key = ecc_table['g']
d = int(private_key_hex, 16)
public_key_hex = temp._kg(d, ecc_table['g'])
print(f"公钥: {public_key_hex}")
print(f"公钥长度: {len(public_key_hex) // 2} 字节")3.3 R+S 格式签名与验签
# 创建 CryptSM2 对象(默认 asn1=False,即 R+S 格式)
csm2 = sm2.CryptSM2(
public_key=public_key_hex,
private_key=private_key_hex,
asn1=False # 默认值,可省略
)
# 准备消息
msg = b"这是一条需要签名的消息"
# 计算 SM3 哈希(gmssl 的 sign 方法需要哈希值,不是原始消息)
msg_hash = sm3_hash([i for i in msg])
print(f"SM3 哈希: {msg_hash}")
# 签名(需要随机数 K)
K = func.random_hex(64)
signature_rs = csm2.sign(bytes.fromhex(msg_hash), K)
print(f"签名(R+S): {signature_rs}")
print(f"签名长度: {len(signature_rs) // 2} 字节") # 64
# 验签
result = csm2.verify(signature_rs, bytes.fromhex(msg_hash))
print(f"验签结果: {result}") # True⚠️ 注意:gmssl 的 sign() 方法接收的 data 参数是 SM3 哈希的字节,不是原始消息。这是很多初学者的第一个坑。
3.4 DER 编码签名与验签
# 创建 CryptSM2 对象(asn1=True,启用 DER 编码)
csm2_der = sm2.CryptSM2(
public_key=public_key_hex,
private_key=private_key_hex,
asn1=True # 启用 DER 编码
)
# 使用相同的随机数 K,确保签名内容一致(仅编码不同)
signature_der = csm2_der.sign(bytes.fromhex(msg_hash), K)
print(f"签名(DER, hex): {signature_der}")
print(f"签名长度: {len(bytes.fromhex(signature_der))} 字节") # 70-72
# DER 格式的验签也用同一个对象
result = csm2_der.verify(signature_der, bytes.fromhex(msg_hash))
print(f"验签结果: {result}") # True3.5 R+S ⟷ DER 双向转换
在实际工程中,最常见的场景是在两种格式之间转换:
from gmssl.sm2 import DerSequence, DerInteger
def rs_to_der(r_hex: str, s_hex: str) -> str:
"""
R+S 格式转 DER 编码
r_hex, s_hex: 64 字符十六进制字符串(32 字节)
返回: DER 编码的十六进制字符串
"""
r = int(r_hex, 16)
s = int(s_hex, 16)
seq = DerSequence([DerInteger(r), DerInteger(s)])
return seq.encode().hex()
def der_to_rs(der_hex: str) -> str:
"""
DER 编码转 R+S 格式
der_hex: DER 编码的十六进制字符串
返回: R+S 格式的十六进制字符串(128 字符,64 字节)
"""
der_bytes = bytes.fromhex(der_hex)
seq = DerSequence()
origin = seq.decode(der_bytes)
r = origin[0]
s = origin[1]
return '%064x%064x' % (r, s)
# === 实际使用 ===
# 从 R+S 提取 R 和 S
r_hex = signature_rs[:64]
s_hex = signature_rs[64:]
# 转换为 DER
signature_der_converted = rs_to_der(r_hex, s_hex)
print(f"R+S -> DER: {signature_der_converted}")
# 转换回 R+S
signature_rs_converted = der_to_rs(signature_der)
print(f"DER -> R+S: {signature_rs_converted}")
print(f"与原签名一致: {signature_rs_converted == signature_rs}") # True3.6 DER 编码长度分析工具
def analyze_der_signature(der_hex: str) -> dict:
"""
解析 DER 编码的 SM2 签名,返回详细信息
"""
der_bytes = bytes.fromhex(der_hex)
result = {
'total_length': len(der_bytes),
'sequence_tag': f'0x{der_bytes[0]:02x}',
}
offset = 1
# 解析长度(支持长格式,虽然 SM2 签名长度 < 128 字节)
if der_bytes[offset] & 0x80:
length_bytes = der_bytes[offset] & 0x7f
offset += 1 + length_bytes
else:
offset += 1
# 解析 R
r_tag = der_bytes[offset]
r_len = der_bytes[offset + 1]
r_bytes = der_bytes[offset + 2: offset + 2 + r_len]
r_val = int.from_bytes(r_bytes, 'big')
result['r_length'] = r_len
result['r_hex'] = r_bytes.hex()
result['r_padded'] = r_len == 33 # 是否补了 0x00
offset += 2 + r_len
# 解析 S
s_tag = der_bytes[offset]
s_len = der_bytes[offset + 1]
s_bytes = der_bytes[offset + 2: offset + 2 + s_len]
s_val = int.from_bytes(s_bytes, 'big')
result['s_length'] = s_len
result['s_hex'] = s_bytes.hex()
result['s_padded'] = s_len == 33 # 是否补了 0x00
return result
# 使用示例
info = analyze_der_signature(signature_der)
print(f"Total length: {info['total_length']} bytes")
print(f"R: {info['r_hex'][:20]}... (padded: {info['r_padded']})")
print(f"S: {info['s_hex'][:20]}... (padded: {info['s_padded']})")3.7 加密密文的格式选择
SM2 加密也有两种格式,由 mode 参数控制:
# mode=0: C1 || C2 || C3(默认,GM/T 0003.4 标准格式)
csm2_mode0 = sm2.CryptSM2(
public_key=public_key_hex,
private_key=private_key_hex,
mode=0
)
csm2_mode1 = sm2.CryptSM2(
public_key=public_key_hex,
private_key=private_key_hex,
mode=1 # C1 || C3 || C2
)
plaintext = b"机密数据:项目代号 Alpha-7"
ct0 = csm2_mode0.encrypt(plaintext)
ct1 = csm2_mode1.encrypt(plaintext)
print(f"mode=0 密文长度: {len(ct0)} 字节") # C1(65) + C2(N) + C3(32)
print(f"mode=1 密文长度: {len(ct1)} 字节") # 相同长度,但 C2/C3 顺序不同
# 各自解密
pt0 = csm2_mode0.decrypt(ct0)
pt1 = csm2_mode1.decrypt(ct1)
print(f"mode=0 解密正确: {pt0 == plaintext}") # True
print(f"mode=1 解密正确: {pt1 == plaintext}") # True
# ⚠️ mode 必须匹配,否则解密失败
pt_cross = csm2_mode0.decrypt(ct1)
print(f"跨 mode 解密: {pt_cross}") # 乱码或错误四、踩坑实录
坑 1:sign/verify 的 data 参数是哈希,不是原始消息
现象:签名时传入原始消息,验签也传原始消息,结果返回 False。
原因:gmssl 的 sign() 和 verify() 方法的 data 参数要求是 SM3 哈希值(32 字节),不是原始消息。这是 gmssl 库的设计决策,它把哈希计算交给了调用者。
# ❌ 错误
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 的值完全错误。
# ❌ 混用
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。
# ❌ 危险!不要这样做
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 前缀,但建议统一去掉,避免混淆。
# 两种方式都可以,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 签名。
# ❌ cryptography 不支持 SM2 密钥
from cryptography.hazmat.primitives.asymmetric import ec
# ec.SM2() → 不存在
# ✅ 用 cryptography 做 SM3 哈希
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.hashes import SM3
digest = hashes.Hash(SM3())
digest.update(b"message")
hash_bytes = digest.finalize()
# ✅ 用 gmssl 做 SM2 签名
csm2 = sm2.CryptSM2(public_key=public_key_hex, private_key=private_key_hex)
sig = csm2.sign(hash_bytes, func.random_hex(64))结论:SM2 操作必须用 gmssl(或 Java BouncyCastle),cryptography 只能配合做 SM3 哈希。两套库的密钥格式不兼容,不能混用。
五、跨系统互操作指南
5.1 Python ⟷ Java
Java 的 BouncyCastle 库默认使用 DER 编码。Python gmssl 默认使用 R+S。对接时在边界处转换:
# Python 端:将 R+S 转为 DER 发给 Java
def sign_for_java(csm2, msg):
"""生成 DER 编码签名,供 Java BouncyCastle 验签"""
csm2_der = sm2.CryptSM2(
public_key=csm2.public_key,
private_key=csm2.private_key,
asn1=True
)
msg_hash = sm3_hash([i for i in msg])
return csm2_der.sign(bytes.fromhex(msg_hash), func.random_hex(64))
# Python 端:验签 Java 的 DER 编码签名
def verify_from_java(csm2, msg, der_sig_hex):
"""验签 Java BouncyCastle 的 DER 编码签名"""
msg_hash_bytes = bytes.fromhex(sm3_hash([i for i in msg]))
csm2_der = sm2.CryptSM2(
public_key=csm2.public_key,
private_key=csm2.private_key,
asn1=True
)
return csm2_der.verify(der_sig_hex, msg_hash_bytes)5.2 Python ⟷ 硬件加密机
大多数硬件加密机(HSM/加密卡)返回的是 R+S 格式签名,因为硬件实现通常直接输出数学结果,不包含 ASN.1 编码逻辑。
# 硬件加密机返回 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 完整的签名格式工具类
"""
SM2 签名格式工具类
支持 R+S 和 DER 编码的相互转换、解析和验证
"""
from gmssl import sm2, func
from gmssl.sm2 import DerSequence, DerInteger
from gmssl.sm3 import sm3_hash
class SM2SignatureUtil:
"""SM2 签名格式工具类"""
def __init__(self, public_key_hex: str, private_key_hex: str = None):
"""
public_key_hex: 公钥(不含 04 前缀)
private_key_hex: 私钥(可选,有私钥才能签名)
"""
self.public_key = public_key_hex
self.private_key = private_key_hex
self.para_len = 64 # SM2 参数长度(十六进制字符数)
def sign_rs(self, msg: bytes) -> str:
"""R+S 格式签名"""
csm2 = self._get_csm2(asn1=False)
msg_hash = sm3_hash([i for i in msg])
K = func.random_hex(64)
return csm2.sign(bytes.fromhex(msg_hash), K)
def sign_der(self, msg: bytes) -> str:
"""DER 编码签名"""
csm2 = self._get_csm2(asn1=True)
msg_hash = sm3_hash([i for i in msg])
K = func.random_hex(64)
return csm2.sign(bytes.fromhex(msg_hash), K)
def verify_rs(self, msg: bytes, sig_hex: str) -> bool:
"""验签 R+S 格式"""
csm2 = self._get_csm2(asn1=False)
msg_hash_bytes = bytes.fromhex(sm3_hash([i for i in msg]))
return csm2.verify(sig_hex, msg_hash_bytes)
def verify_der(self, msg: bytes, sig_hex: str) -> bool:
"""验签 DER 编码"""
csm2 = self._get_csm2(asn1=True)
msg_hash_bytes = bytes.fromhex(sm3_hash([i for i in msg]))
return csm2.verify(sig_hex, msg_hash_bytes)
def rs_to_der(self, sig_rs_hex: str) -> str:
"""R+S 转 DER"""
r = int(sig_rs_hex[:64], 16)
s = int(sig_rs_hex[64:], 16)
seq = DerSequence([DerInteger(r), DerInteger(s)])
return seq.encode().hex()
def der_to_rs(self, sig_der_hex: str) -> str:
"""DER 转 R+S"""
der_bytes = bytes.fromhex(sig_der_hex)
seq = DerSequence()
origin = seq.decode(der_bytes)
return '%064x%064x' % (origin[0], origin[1])
def parse_der(self, sig_der_hex: str) -> dict:
"""解析 DER 编码签名"""
der_bytes = bytes.fromhex(sig_der_hex)
result = {'total_length': len(der_bytes)}
assert der_bytes[0] == 0x30, "不是有效的 DER SEQUENCE"
offset = 2 # 跳过 SEQUENCE tag + length(假设长度 < 128)
# 解析 R
assert der_bytes[offset] == 0x02, "R 不是 INTEGER 类型"
r_len = der_bytes[offset + 1]
r_bytes = der_bytes[offset + 2: offset + 2 + r_len]
result['r'] = r_bytes.hex()
result['r_padded'] = r_len == 33
offset += 2 + r_len
# 解析 S
assert der_bytes[offset] == 0x02, "S 不是 INTEGER 类型"
s_len = der_bytes[offset + 1]
s_bytes = der_bytes[offset + 2: offset + 2 + s_len]
result['s'] = s_bytes.hex()
result['s_padded'] = s_len == 33
return result
def _get_csm2(self, asn1: bool) -> sm2.CryptSM2:
kwargs = {
'public_key': self.public_key,
'asn1': asn1
}
if self.private_key:
kwargs['private_key'] = self.private_key
return sm2.CryptSM2(**kwargs)
# === 使用示例 ===
util = SM2SignatureUtil(public_key_hex, private_key_hex)
msg = b"这是一条测试消息"
# R+S 签名
sig_rs = util.sign_rs(msg)
print(f"R+S: {sig_rs[:40]}...")
# DER 签名
sig_der = util.sign_der(msg)
print(f"DER: {sig_der[:40]}...")
# 格式转换:R+S → DER → R+S 往返验证
sig_der_from_rs = util.rs_to_der(sig_rs)
sig_rs_roundtrip = util.der_to_rs(sig_der_from_rs)
assert sig_rs_roundtrip == sig_rs, "R+S → DER → R+S 往返不一致"
print("格式转换往返验证通过: R+S → DER → R+S ✓")
# 格式转换:DER → R+S → DER 往返验证
sig_rs_from_der = util.der_to_rs(sig_der)
sig_der_roundtrip = util.rs_to_der(sig_rs_from_der)
assert sig_der_roundtrip == sig_der, "DER → R+S → DER 往返不一致"
print("格式转换往返验证通过: DER → R+S → DER ✓")
# 解析 DER
info = util.parse_der(sig_der)
print(f"R padded: {info['r_padded']}, S padded: {info['s_padded']}")七、总结
关键要点
- 两种格式本质相同:都是 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 证书 | DER | X.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《信息安全技术 网络安全等级保护基本要求》中的密码学要求
参考来源
- GM/T 0003.2-2012《SM2 椭圆曲线公钥密码算法 第 2 部分:数字签名算法》
- GM/T 0003.4-2012《SM2 椭圆曲线公钥密码算法 第 4 部分:公钥加密算法》
- GM/T 0009-2012《SM2 密码算法使用规范》
- GB/T 35275-2017《信息安全技术 SM2 密码算法加密签名消息语法规范》
- GB/T 35275-2026《网络安全技术 SM2 密码算法加密签名消息格式》(2026-05-25 发布,2026-12-01 实施)
- GM/T 0024-2014《SSL VPN 技术规范》
- gmssl Python 库
- 信安世纪参与 GB/T 33560-2026 和 GB/T 47687-2026 编制
- 格尔软件牵头修订 GB/T 35275-2026
- 全国标准信息公共服务平台 - GB/T 35275-2026