SM2 公钥加密算法深度解析:从 C1C3C2 格式到完整性验证

算法原理 · 2026-08-11


title: "SM2 公钥加密算法深度解析:从 C1C3C2 格式到完整性验证" slug: "sm2-encryption-algorithm" excerpt: "SM2 公钥加密是三大核心功能之一,采用混合加密方案实现机密性与完整性保障。本文深入剖析 GM/T 0003.4-2012 定义的加密/解密流程,详解 C1C3C2 密文格式的构造原理,分析 ZA 预处理在加密中的作用,并与 RSA-OAEP 进行安全性对比,最终提供可验证的 Python 实现。" category: algorithm tags: - SM2 - 公钥加密 - GM/T 0003.4 - 混合加密 - C1C3C2

概述

SM2 是国家密码管理局发布的椭圆曲线公钥密码算法标准,包含数字签名、密钥交换和公钥加密三大功能。相比 RSA,SM2 以 256 位密钥提供约 128 位安全强度,密钥长度仅为 RSA-2048 的 1/8。

公钥加密是 SM2 最常被忽视的功能。GM/T 0003.4-2012 详细定义了 SM2 加密算法,其核心是椭圆曲线混合加密方案:用 ECC 协商临时密钥,用 SM4 加密数据,用 SM3 验证完整性。这种设计既保持了公钥加密的密钥分发优势,又具备对称加密的高效性。

数学基础

曲线参数

SM2 使用 NIST P-256 同构的曲线,但参数选择有国密特色:

CODE
曲线方程: y² = x³ + ax + b (mod p)
|a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC|
|b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93|
|基域素数 p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF|
|生成元 G = (xG, yG)|
|xG = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7|
|yG = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0|
|阶 n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123|
 cofactor h = 1

曲线参数通过确定性选择(SEC 1 v2 第 B.3.1 节方法),确保无可疑构造。

ZA 杂凑值

SM2 加密使用 ZA 值绑定用户身份:

$$ZA = SM3(ENTL \parallel ID \parallel a \parallel b \parallel x_G \parallel y_G \parallel x_A \parallel y_A)$$

其中:

  • ENTL:用户 ID 长度(比特),2 字节大端
  • ID:用户身份标识(ASCII 字符串)
  • a, b:曲线参数
  • (xG, yG):生成元坐标
  • (xA, yA):用户公钥坐标
ZA 的作用是防止身份混淆攻击:攻击者用用户 A 的公钥加密消息后,冒充用户 B 解密,从而泄露 A 的机密信息。ZA 将身份信息"固化"到加密过程中。

加密算法

算法流程

SM2 公钥加密采用 C1C3C2 格式(注意不是 C1C2C3):

C1C3C2 格式详解

密文结构:

CODE
| C1 (64 字节) | C3 (32 字节) | C2 (klen 字节) |
|--------------|--------------|----------------|
| (x1, y1)     | 哈希值       | 加密后的明文   |
  • C1:椭圆曲线点 (x1, y1),每个坐标 32 字节,共 64 字节
  • C3:SM3 杂凑值,用于完整性验证,32 字节
  • C2:加密后的明文,长度与原消息相同
为什么是 C1C3C2 而不是 C1C2C3?

这是 SM2 与国际标准的显著差异。C1C3C2 格式允许接收方在解密前先验证完整性,而不必先恢复明文。具体流程:

CODE
解密时:
1. 计算 [dA]C1 = (x2, y2)
2. t = KDF(x2 || y2, klen)
3. C2' = C2 ⊕ t  ← 先恢复明文
4. C3' = SM3(x2 || C2' || y2)  ← 重新计算哈希
5. 验证 C3 == C3',验证通过才输出明文

如果采用 C1C2C3 格式,接收方需要先恢复明文才能验证哈希。C1C3C2 虽然顺序不同,但语义等价。

解密算法

算法流程

完整性验证

SM2 加密同时提供机密性和完整性保障:

  • 机密性:由椭圆曲线 Diffie-Hellman 密钥协商提供,攻击者无法从密文恢复共享密钥
  • 完整性:由 SM3 杂凑值 C3 提供,攻击者篡改密文后验证失败
与 AES-GCM 类似,SM2 加密是认证加密(Authenticated Encryption)方案。但实现方式不同:AES-GCM 使用 GCM 模式内部集成认证,SM2 使用" Encrypt-then-MAC "范式。

KDF 密钥派生函数

SM2 使用自定义 KDF 函数派生加密密钥:

KDF 使用 SM3 杂凑函数,以 32 字节为块迭代派生密钥流。这与 NIST SP 800-56C 推荐的 HKDF 不同,但安全性相当。

安全强度分析

密钥长度对比

方案密钥长度安全强度密文膨胀率
SM2-256256 bit128 bit~50% (32字节C3 + 64字节C1)
RSA-20482048 bit112 bit~50% (256字节密文)
RSA-30723072 bit128 bit~67% (384字节密文)
SM2 的密文膨胀率:
  • 加密 1 字节明文 → 密文 97 字节(C1:64 + C3:32 + C2:1)
  • 加密 100 字节明文 → 密文 132 字节
  • 加密 1000 字节明文 → 密文 1064 字节
对于短消息,SM2 的膨胀率较高;对于长消息,膨胀率趋近于 0。

已知攻击

攻击类型复杂度适用场景
暴力破解O(2^256)私钥恢复
Pollard's rhoO(2^128)ECDLP 求解
MOV 归约不适用sm2p256v1 嵌入度大
异常曲线攻击不适用曲线参数已验证
共有因数攻击O(1)多用户共享曲线
目前对 SM2 加密最有效的攻击是 Pollard's rho 算法,复杂度 O(√n) ≈ O(2^128)。

与 RSA-OAEP 对比

特性SM2 加密RSA-OAEP
密钥长度256 bit2048+ bit
安全强度128 bit112-128 bit
密文膨胀~50%~50%
确定性否(随机 k)否(随机填充)
完整性保障是(C3)否(需额外 HMAC)
标准来源GM/T 0003.4PKCS#1 v2.2
SM2 加密在完整性保障方面优于 RSA-OAEP,后者需要额外添加 HMAC 才能提供认证加密。

实现示例

Python 实现(基于 gmssl)

运行输出示例

CODE
明文: b'Hello, SM2 Encryption!'
明文长度: 22 字节
密文(hex): 04d6aa8734bf6be5e1e7d1c2f6c6f7e8d9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e60f5a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f60f5a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6073a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3

密文结构:
  C1 (椭圆曲线点): 04d6aa8734bf6be5e1e7d1c2f6c6f7e8...
  C3 (SM3哈希): 0f5a7b8c9d0e1f2a3b4c5d6e7f8a9b0c...
  C2 (加密明文): 1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6...

解密结果: b'Hello, SM2 Encryption!'
解密验证: ✓ 成功

标准引用

本文涉及的标准:

标准编号标准名称内容
GM/T 0003.4-2012SM2 密码算法使用规范 第4部分:公钥加密算法定义 SM2 加密算法
GB/T 32918.4-2016信息安全技术 SM2 椭圆曲线公钥密码算法 第4部分:公钥加密算法国家标准版本
GM/T 0003.1-2012SM2 密码算法使用规范 第1部分:总则曲线参数定义

常见错误

错误 1:混淆 C1C2C3 与 C1C3C2 格式

一些实现错误地将密文存储为 C1C2C3 格式,导致与其他系统不兼容。标准规定为 C1C3C2,实现时需注意:

PYTHON
# 错误:C1C2C3 格式
wrong_ciphertext = c1 + c2 + c3

# 正确:C1C3C2 格式
correct_ciphertext = c1 + c3 + c2

错误 2:忽略 ZA 值绑定

省略 ZA 计算会导致身份混淆攻击。攻击者可以:

  • 用用户 A 的公钥加密消息
  • 发送给攻击者控制的服务器
  • 服务器用自己的私钥解密,获得 A 的机密信息
正确做法:加密和解密双方都必须计算并验证 ZA 值。

错误 3:使用 SECP256R1 代替 SM2

SM2 曲线与 NIST P-256(SECP256R1)同构,但参数选择方式不同。使用 SECP256R1 实现:

  • 密钥长度兼容,但无法通过密评
  • 不符合 GM/T 0003-2012 标准要求
  • 可能被认定为"非合规近似实现"
正确做法:使用 gmssl、Tongsuo 等支持 SM2 的国密库。

应用场景

SM2 公钥加密适用于以下场景:

场景说明
数据传输加密保护通信链路上的敏感数据
文件加密存储加密存储的敏感文件
密钥封装封装对称密钥(如 SM4 密钥)
数字信封与 SM4 结合实现混合加密

总结

SM2 公钥加密算法是国密体系的重要组成部分,采用混合加密方案实现机密性与完整性双重保障。核心要点:

  • C1C3C2 格式:密文结构为椭圆曲线点 || SM3哈希 || 加密明文
  • ZA 绑定:用户身份绑定到加密过程,防止身份混淆攻击
  • 完整性保障:通过 SM3 杂凑值 C3 实现认证加密
  • 安全强度:256 位密钥提供 128 位安全强度,与 RSA-3072 相当
实现时需注意格式兼容性和 ZA 值计算,避免常见错误。

相关实践

参考

  • GM/T 0003.4-2012《SM2 密码算法使用规范 第4部分:公钥加密算法》
  • GB/T 32918.4-2016《信息安全技术 SM2 椭圆曲线公钥密码算法 第4部分:公钥加密算法》
  • 《GMT 0003-2012 SM2椭圆曲线公钥密码算法》第4部分
  • NIST SP 800-56C Rev. 2《Recommendation for Pair-Wise Key-Establishment Schemes Using Integer Factorization Cryptography》
  • RFC 8017《PKCS #1: RSA Cryptography Specifications Version 2.2》