密码学基础
阅读 SM2 密钥协商协议之前,需要先理解它依赖的几个基础概念:对称加密、哈希、 有限域、椭圆曲线、ECDH、前向安全。本页按「直觉 → 数学 → 工程」的顺序 把这些知识一次讲清,不预设读者有密码学背景。读完本页再看 SM2 密钥协商,协议里的每个字段都会有明确来历。
密码学解决什么问题
信息在网络上传输会面临四类威胁,密码学为每类威胁提供了对应工具:
| 安全目标 | 含义 | 主要工具 | 本项目对应 |
|---|---|---|---|
| 机密性 | 第三方看不懂内容 | 对称 / 非对称加密 | SM4-GCM 业务通信 |
| 完整性 | 内容没被篡改 | 哈希、MAC、AEAD Tag | GCM 认证标签 |
| 身份认证 | 确认对方确实是它 | 数字签名、证书、MAC | SM2 签名 + 身份杂凑 |
| 不可否认 | 发过的事不能抵赖 | 数字签名 | SM2withSM3 签名 |
注意一个常见误解:加密只保证机密性,不自动保证完整性。一条被篡改过的 密文,解密时可能照样吐出一堆字节——所以现代工程优先选择 AEAD(同时提供 加密与认证的模式),本项目选的 SM4-GCM 就是 AEAD。
密码学家族全景
对称与非对称最核心的区别:
- 对称加密:加密解密用同一把密钥。速度极快,适合大量数据;难点是 「通信前怎么安全地把同一把钥匙交给对方」;
- 非对称加密:每人一对密钥,公钥公开、私钥保密,公钥加密只有私钥 能解,私钥签名任何人可用公钥验证。解决了密钥分发与身份问题,但速度慢 几个数量级,不适合直接加密大量数据。
实际系统(包括本项目)总是把两者组合:用非对称密码协商出一把对称密钥, 再用对称密钥加密业务数据。
对称加密与工作模式
SM4 分组密码
SM4 是我国商用分组密码标准(GM/T 0002、GB/T 32907),结构与 AES 同级:
| 参数 | 值 |
|---|---|
| 分组长度 | 128 bit(16 字节) |
| 密钥长度 | 128 bit(16 字节) |
| 轮数 | 32 轮 |
| 结构 | 非平衡 Feistel,每轮含非线性 S 盒变换与线性 L 变换 |
分组密码本身只能加密「恰好一个分组」的数据。要加密任意长度的消息,需要 工作模式规定多分组如何处理。
ECB:最简单也最危险
ECB(电子密码本)把每个分组独立加密。问题立竿见影:相同明文分组产生 相同密文分组,数据的轮廓会泄露。经典演示是加密一张位图,密文图片里仍能 看清图案:
明文分组 密文分组
[A][A][B] [X][X][Y] ← 两个 A 变成相同的两个 X,模式暴露2
工程上 ECB 只用于加密互不相关的单个密钥(如密钥包装的特例),绝不用于 通信数据。
CBC:链式但有短板
每个明文分组先与「上一个密文分组」异或再加密,第一个分组与随机 IV 异或。 相同明文因 IV 不同而密文不同。缺点:加密必须串行(不能并行提速);需要 填充,历史上大量系统因 Padding Oracle 攻击被攻破。
CTR:把分组密码变成流密码
加密双方共享一个「从随机 nonce 开始递增的计数器」,SM4 只加密计数器本身 得到密钥流,再与明文异或。优点:加密解密完全相同、可并行、无需填充。 但 CTR 只提供机密性,没有完整性——攻击者翻转密文的一个比特,解密后的 对应比特也会翻转,而收发双方毫无察觉。
GCM:本项目的选择
GCM = CTR 加密 + GMAC 认证,是一种 AEAD:
调用 SM4-GCM 的输入输出:
| 输入 | 说明 |
|---|---|
| 密钥 K | 16 字节,由 SM2 协商得到 |
| IV/nonce | 12 字节为标准推荐长度(本项目帧内使用 16 字节 IV) |
| 明文 | 任意长度 |
| AAD | 只认证不加密的附加数据(可空) |
输出 = 密文 + Tag(本项目为 16 字节)。解密时先重新计算并比对 Tag, 只有 Tag 正确才输出明文——密文被篡改时 Tag 校验失败,直接拒绝。
nonce 重用是 GCM 的致命禁忌:同一把密钥下若两个消息使用相同 IV, 攻击者不仅能还原明文,还能进一步恢复认证子密钥、伪造任意 Tag。因此 工程约定每帧随机 IV,且会话密钥有 TTL(本项目 Redis 侧 7 天)。
哈希函数、HMAC 与 KDF
哈希函数
哈希函数把任意长度输入压缩成固定长度「指纹」(SM3 输出 256 bit = 32 字节)。 安全的哈希满足:
- 单向性:由输出反推输入在计算上不可行;
- 抗第二原像:给定一个输入,找不到另一个输入有相同输出;
- 抗碰撞:找不到任意两个不同输入有相同输出。
因此两份数据的哈希值相同,就可以认为数据相同——完整性校验、证书指纹、 区块链都建立在这个性质上。
SM3
SM3(GM/T 0004、GB/T 32905)输出 256 bit,内部对 512 bit 分组做 64 轮压缩, 安全强度与 SHA-256 相当。本项目中它承担三重角色:SM2 签名的杂凑、密钥 派生 KDF 的核心、HMAC 的底层函数。
HMAC:带密钥的哈希
普通哈希人人可算,只能证明「内容没被意外改坏」;要证明「内容来自持有密钥 的一方」,需要消息认证码。HMAC 的标准构造不是简单的 Hash(key‖message) (这种朴素写法对 SM3/SHA 这类 Merkle–Damgård 结构存在长度扩展攻击),而是:
HMAC(K, m) = H( (K ⊕ opad) ‖ H( (K ⊕ ipad) ‖ m ) )即对内层、外层各做一次哈希并用两个填充常数隔离。本项目 TOKEN 帧里的 HMAC(ID_A ‖ expiration) 就是用协商出的会话密钥做 HMAC-SM3。
KDF:从共享秘密派生密钥
ECDH 算出的原始共享点结构特殊(是椭圆曲线点,且各 bit 分布不均匀),不能 直接当密钥使用。KDF(密钥派生函数)把它重新「搅碎」成均匀的定长密钥串。 SM2 协议使用的 KDF 是计数器模式的 SM3:
KDF(Z, klen) = SM3(Z ‖ counter=1) ‖ SM3(Z ‖ counter=2) ‖ ... 截取前 klen 位非对称加密:为什么选择椭圆曲线
两种主流非对称体系对比:
| RSA / 整数分解 | ECC / 椭圆曲线离散对数 | |
|---|---|---|
| 数学难题 | 大整数分解 | 椭圆曲线离散对数问题 ECDLP |
| 128 位安全强度所需密钥 | 约 3072 bit | 约 256 bit |
| 签名/密钥长度 | 长 | 短(约为 RSA 的 1/12) |
| 计算与存储开销 | 大 | 小 |
| 移动端友好性 | 一般 | 好 |
在算力、电量、带宽都受限的移动终端上,256 bit 的椭圆曲线密钥即可达到 3072 bit RSA 的安全强度,这正是华为 Pad 场景选择 SM2 的工程理由。要理解 它为什么安全,需要先补三块数学:群、有限域、椭圆曲线上的点运算。
数学前置
群:一种「运算结构」
群是一个集合 G 配上一种运算「·」,满足:
- 封闭性:a, b ∈ G,则 a·b ∈ G;
- 结合律:(a·b)·c = a·(b·c);
- 单位元:存在 e 使 e·a = a;
- 逆元:每个 a 都有逆元 a⁻¹,使 a·a⁻¹ = e。
若还满足交换律 a·b = b·a,称为阿贝尔群。整数与加法构成群(单位元 0, 逆元 −n);非零有理数与乘法也构成群。密码学关心的是:椭圆曲线上的点 配上「点加法」恰好构成一个阿贝尔群——于是所有群的代数工具都能用上。
有限域:做「时钟算术」
密码学不能用实数(无法在计算机里精确表示、还会泄露信息),全部运算都在 有限域上进行。最直观的是素数域 Fp:集合 {0, 1, 2, …, p−1},所有运算 做完都对素数 p 取模,像时钟走到 p 就回到 0:
加法 (a + b) mod p
减法 (a − b) mod p (加上 p 防止负数)
乘法 (a × b) mod p
除法 a × b^(p−2) mod p (费马小定理保证 b^(p−1)=1,故 b^(p−2) 是 b 的逆元)2
3
4
因为 p 是素数,集合里每个非零元素都有唯一乘法逆元,四则运算完整闭合—— 这就是后面椭圆曲线坐标虽然只取整数、却仍能做「除法」的原因。SM2 使用的 p 是一个 256 bit 的大素数。
椭圆曲线与点加法
密码学中的椭圆曲线指满足(短)魏尔斯特拉斯方程的点集:
y² = x³ + a x + b 要求 4a³ + 27b² ≠ 0(保证曲线光滑、无自交点)先在实数平面上认识它的形状:
两个直观特征:
- 方程中 y 以平方出现,因此曲线关于 x 轴对称:若 (x, y) 在曲线上, (x, −y) 也在曲线上;
- 曲线不是椭圆,名字来源于历史上对椭圆周长积分的研究。
点加法用几何方式定义。给定曲线上两点 P、Q:
- 过 P、Q 作一条直线(弦),它与曲线恰有第三个交点,记为 −R;
- 将 −R 关于 x 轴镜像(取负 y 坐标),得到 R;
- 定义 P + Q = R。
当 P、Q 重合(过 P 作切线),同样的规则给出倍点 [2]P:
另外定义一个虚拟点无穷远点 O 作为单位元:P + O = P;互为镜像的两个点 相加等于 O,即 P + (−P) = O。
可以证明这样定义的点加法满足结合律与交换律——曲线上的全部点(含 O) 构成阿贝尔群。实际计算时不需要画图,代数公式如下(坐标在实数或 Fp 中 形式相同,除法在 Fp 中换成乘逆元):
P = (x1, y1), Q = (x2, y2), P ≠ ±Q
λ = (y2 − y1) / (x2 − x1)
x3 = λ² − x1 − x2
y3 = λ(x1 − x3) − y1
P + Q = (x3, y3)
倍点 P = Q:
λ = (3x1² + a) / (2y1)
x3 = λ² − 2x1
y3 = λ(x1 − x3) − y12
3
4
5
6
7
8
9
10
标量乘法与离散对数难题
有了点加法,就能定义「点的整数倍」:
[k]P = P + P + … + P(k 个)工程上计算 [k]P 并不需要加 k 次,用「倍点 + 加法」(类似二进制快速幂), 约 log₂k 步即可完成,k 取 256 bit 时只需几百次点运算。但反向问题—— 已知点 P 与 Q = [k]P,求整数 k——在一般椭圆曲线上没有已知的快速解法, 这就是椭圆曲线离散对数问题(ECDLP):
| 方向 | 难度 |
|---|---|
| 正向:已知 k、P 算 [k]P | 很快(毫秒级) |
| 反向:已知 P、[k]P 算 k | 256 bit 曲线下,全球算力也不可行 |
密码学的安全性就建立在这种「正反向难度不对称」上。
有限域上的曲线
真实密码系统把上述全部内容搬到 Fp 上:点坐标 (x, y) 都是 0…p−1 的整数, 点加法公式里的除法取模逆元。实数平面上那条光滑曲线,在 Fp 上变成一堆 看似随机散布的点,但群结构完整保留。还要约定:
| 量 | 含义 |
|---|---|
| 基点 G | 协议中所有人共用的一个标准生成点 |
| 阶 n | 满足 [n]G = O 的素数(G 生成的子群大小),私钥必须取自 [1, n−1] |
| 余因子 h | 曲线上总点数与 n 的比值,SM2 中 h = 1 |
SM2 曲线参数(sm2p256v1)
SM2 使用一条 256 bit 素数域上的固定曲线,参数由 GM/T 0003.5 给出并全网 统一(不需要、也不允许自行选曲线):
p = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
a = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
Gx = 32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
Gy = BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
n = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
h = 12
3
4
5
6
7
ECDH:椭圆曲线 Diffie–Hellman
有了标量乘法与 ECDLP,就能构造密钥交换。经典 ECDH 过程:
关键在代数上:
A 算 [a]PB = [a]([b]G) = [ab]G
B 算 [b]PA = [b]([a]G) = [ba]G = [ab]G (标量乘法可交换)2
全程公开线路上只有 G、PA、PB。窃听者即使拿到 PA、PB,由 ECDLP 也求不出 a 或 b,因而算不出共享点。一个常用类比是调色:双方公开交换「混好的 颜色」,但无法从混合色逆向分离出秘方原色。
朴素 ECDH 的缺陷:中间人
ECDH 只保证「窃听者算不出」,却不保证「对方是谁」。攻击者 C 可以分别与 A、B 各做一次 ECDH:
要堵住这个洞,必须让公钥携带身份:每方持有长期密钥(私钥由本人保管、 公钥经可信渠道或证书确认),并对交换内容签名。本项目的做法更进一步, 把身份杂凑 ZA/ZB 直接放入共享密钥的派生输入(见握手页),使「这把密钥 确实是我和你之间的」成为数学事实,而不是 ECDH 之外的附加声明。
前向安全
如果长期私钥直接参与密钥交换,一旦它日后泄露,历史上录下来的全部密文都 可被解密。现代协议因此采用临时密钥:
- 长期密钥只用于签名、证明身份;
- 每次会话现场生成一对一次性临时密钥(rA, RA),协商完成立即销毁临时私钥;
- 这样即使长期私钥将来泄露,也无法还原任何历史会话密钥——历史会话永久 安全,称为前向安全(Forward Secrecy)。
本项目 SM2 握手中的 rA/rB、RA/RB 就是临时密钥对;长期密钥 dA/dB 只出现在 中间参数 tA/tB 与签名中。
证书与 PKI(直觉)
浏览器怎么知道一个公钥确实属于它声称的主人?靠 PKI(公钥基础设施):
- 受信任的证书机构(CA)用自己的私钥为「域名 + 公钥」签发 X.509 证书;
- 设备出厂内置一组可信 CA 根证书;访问网站时服务器出示证书链,设备用根 证书逐级验证签名,并检查证书绑定的域名与当前站点一致;
- 自签证书没有经过任何受信任 CA,设备无法确认其持有者身份,因此默认 拒绝——这不是「证书坏了」,而是「无人担保」。本项目外网测试服使用 自签证书时,终端会明确告知风险并由用户知情后选择是否跳过。
小结:协议设计的必然性
把本页串起来,可以理解 SM2 握手协议为什么长成那样:
- 要加密大量业务数据 → 用对称的 SM4;
- 但通信前没有共享密钥 → 用非对称的 ECDH 现场协商;
- 朴素 ECDH 不认证对方 → 引入长期密钥、签名与身份杂凑 ZA/ZB;
- 担心长期密钥泄露波及历史会话 → 改用临时密钥获得前向安全;
- 只加密不防篡改不够 → 用 GCM 同时出密文与 Tag;
- 原始共享点不能直接当密钥 → 用计数器 SM3 的 KDF 派生。