首页/安全/等保与合规速查/SM2 公钥加密与密文格式互操作
密码与认证合规50 条

SM2 公钥加密与密文格式互操作

SM2 公钥加密用于保护会话密钥或小段数据;不同实现的密文分量顺序可能不同(国密规范为 C1||C3||C2),跨实现对接必须核对格式。

等保与合规速查安全更新于 2026-10-13

SM2 加密用在哪

SM2 是基于椭圆曲线的国密公钥算法,标准中同时规定了数字签名、密钥交换与公钥加密三类用法。公钥加密典型用途是保护会话密钥或小段敏感数据:用对方公钥加密一个随机生成的对称密钥,再用 SM4 之类的对称算法加密业务数据,这就是信封结构。它的实际难点不在算法本身,而在密文格式的互操作——不同实现的分量顺序与编码约定不同,跨系统对接时必须先对齐格式,否则解密直接失败。

基本信息

标准GB/T 32918 系列(第 4 部分为公钥加密算法)
密钥长度256 位,公钥为 64 字节点坐标(未压缩编码时带 04 前缀共 65 字节)
密文构成C1(椭圆曲线点)+ C3(摘要值,32 字节)+ C2(与明文等长的加密数据)
国标顺序C1‖C3‖C2
常见库OpenSSL(1.1.1 起支持 SM2)、GmSSL、Python gmssl、各语言国密 SDK
适用长度适合加密密钥等小块数据,不适合直接加密大文件

Python(gmssl)示例

Python(gmssl)示例
from gmssl import sm2

# 公私钥均为十六进制字符串(私钥 64 位十六进制,公钥 128 位)
crypt = sm2.CryptSM2(public_key=pub_hex, private_key=priv_hex)

ct = crypt.encrypt(b"session-key-01")   # 加密,返回 bytes 密文
pt = crypt.decrypt(ct)                  # 解密,注意输入类型要与实现约定一致
assert pt == b"session-key-01"          # 自测:解密结果与原文一致

# 互操作注意:不同库/版本的分量顺序(C1C3C2 或 C1C2C3)、
# C1 是否带 04 前缀、是否输出十六进制字符串都可能不同,
# 对接前先确认对方约定,必要时按约定做顺序转换与补/去前缀

互操作检查项

检查项可能差异处理方式
分量顺序C1C3C2(国标)与 C1C2C3(部分库默认)明确约定并做转换,写入接口文档
C1 编码是否带未压缩点前缀 04按对方约定补齐或截去,长度校验放在解密前
输出编码原始字节数组与十六进制字符串传输层统一为 Base64 或十六进制并写进接口说明
KDF 参数标准实现应使用 SM3 派生,参数不可自定义不自造派生函数,否则仅在本系统内能解开
密钥用途加密密钥对与签名密钥对混用按用途分设密钥对,避免一处泄露影响两类操作
加密前必须与对方核对密文顺序与编码约定,否则解密失败;这类问题在联调阶段表现为「同样的库、同样的密钥却解不开」,排查成本很高。建议把约定写进接口文档并保留一组互通测试向量。
SM2 加密依赖随机数 k:随机质量不足会导致密钥可推导。使用成熟库的默认随机源,不要自行拼接时间戳或计数器作为随机数。

易错点

  • 用 SM2 直接加密大文件:加解密性能不可接受,正确做法是加密数据密钥再由 SM4 处理数据
  • 忽略随机数质量:弱随机导致私钥可从密文推导,加密形同明文
  • 忽略密文分量顺序与编码差异:跨实现对接时解密失败,且错误信息通常无指向性
  • 加密密钥对与签名密钥对混用:一旦一侧泄露,另一侧用途同时失守

数据来源与参考

  • GB/T 32918.4-2016
  • openEuler / GMSSL 社区文档
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌