公钥密码基础(十):SM2:国密公钥密码算法初步

前言

SM2 是我国标准体系中的椭圆曲线公钥密码算法,覆盖数字签名、公钥加密和密钥交换等功能。它建立在素数有限域上的椭圆曲线点群之上,因此仍然离不开前面文章介绍的几个基础对象:

  • 曲线参数和有限域;
  • 基点 G G G 及其阶 n n n
  • 私钥标量 d d d
  • 公钥点 P = [ d ] G P=[d]G P=[d]G
  • 模逆、点加法和标量乘法。

但 SM2 不是“把 ECDSA 的名字换成 SM2”,也不是“把 ECDH 的曲线参数换一下”。SM2 在签名中引入了用户身份摘要 Z A Z_A ZA,在加密中规定了基于 KDF 的密文结构,在密钥交换中加入身份绑定和确认信息。这些细节如果遗漏,公式即使能运行,也可能与标准实现不兼容或失去协议安全性。

本文以 SM2 的整体结构为主线,介绍签名、公钥加密和密钥交换的核心流程,重点解释用户身份哈希、签名中的随机数、加密 KDF 和工程实现中的编码问题。具体参数和编码应以使用的国家标准、行业标准及密码库文档为准。

文中公式用于理解协议结构。SM2 的生产实现应使用经过审计的国密密码库,不要根据本文手写一套用于实际业务的密码实现。

现代密码学 专栏:
https://blog.csdn.net/r_feynman_/category_13190241.html

Crypto 密码解析实战靶场:https://blog.csdn.net/r_feynman_/category_13194584.html


一、SM2 的数学基础

1.1 曲线和基点

SM2 使用椭圆曲线点群。抽象地表示为:

E ( F p ) : y 2 ≡ x 3 + a x + b ( m o d p ) E(\mathbb F_p):\quad y^2\equiv x^3+ax+b\pmod p E(Fp):y2x3+ax+b(modp)

选定基点 G G G,其阶为 n n n,并有:

n G = O nG=\mathcal O nG=O

用户 A A A 选择私钥:

d A ∈ { 1 , 2 , … , n − 2 } d_A\in\{1,2,\ldots,n-2\} dA{1,2,,n2}

计算公钥:

P A = [ d A ] G P_A=[d_A]G PA=[dA]G

不同标准会对曲线域参数、点坐标长度、参数命名和编码给出明确规定,不能只根据“都是 256 位椭圆曲线”判断两个实现相互兼容。


1.2 标量与坐标的两个模数

和其他 ECC 协议一样,SM2 中有两个重要模数:

  • 坐标和曲线方程在 F p \mathbb F_p Fp 中运算;
  • 私钥、临时随机数、签名 r , s r,s r,s 等标量通常在模 n n n 的子群中运算。

例如,点乘中的坐标计算使用模 p p p,但签名中的:

r = ( e + x 1 )   m o d   n r=(e+x_1)\bmod n r=(e+x1)modn

使用的是模 n n n。把这里的 n n n 错写成 p p p,会得到看似合理但完全不兼容的签名。


1.3 SM2 的三个功能

SM2 标准体系通常涉及三类流程:

  1. 数字签名:证明签名者持有私钥,并绑定消息和用户身份;
  2. 公钥加密:使用接收者公钥保护消息机密性,并通过摘要检查完整性;
  3. 密钥交换:双方通过临时密钥和长期密钥共同计算共享密钥,并可进行身份确认。

三者底层都使用椭圆曲线点乘,但输入、临时量、哈希和输出格式不同,不能把某一个流程的公式直接套到另一个流程。

在这里插入图片描述

图1:SM2三类功能共享ECC点乘骨架图解
SM2签名验签、公钥加密、密钥交换三大功能复用同一套ECC标量乘法底层:私钥 d A d_A dA与基点 G G G执行标量乘法 P A = [ d A ] G P_A=[d_A]G PA=[dA]G计算得到公钥 P A P_A PA;公钥生成后,向上支撑签名验证、公钥加密、密钥交换三类上层能力,工程上可复用点加、点倍的核心代码。

在这里插入图片描述

图2:两个模数不能混用图解
SM2存在两套独立模运算空间,不可混淆:左侧模 p p p用于曲线相关运算,点加、点倍、曲线方程 y 2 = x 3 + a x + b ( m o d p ) y^2=x^3+ax+b \pmod{p} y2=x3+ax+b(modp)、曲线上点坐标都在 F p \mathbb F_p Fp下计算;右侧模 n n n用于标量相关运算,私钥 d d d、随机数 k k k、签名分量 ( r , s ) (r,s) (r,s)、哈希值 e e e等标量,签名公式均在   m o d     n \bmod\ n mod n中计算。


二、SM2 签名中的用户身份摘要 Z A Z_A ZA

2.1 为什么签名不只哈希消息

普通 ECDSA 通常对消息计算:

e = H ( M ) e=H(M) e=H(M)

SM2 签名会先把用户身份、曲线参数和公钥绑定起来,生成用户身份摘要:

Z A = H v ( ENTL ⁡ ∥ I D A ∥ a ∥ b ∥ x G ∥ y G ∥ x A ∥ y A ) Z_A=H_v(\operatorname{ENTL}\mathbin\|ID_A\mathbin\|a\mathbin\|b\mathbin\|x_G\mathbin\|y_G\mathbin\|x_A\mathbin\|y_A) ZA=Hv(ENTLIDAabxGyGxAyA)

其中:

  • H v H_v Hv 是标准规定的哈希算法;
  • I D A ID_A IDA 是签名者的用户身份标识;
  • ENTL ⁡ \operatorname{ENTL} ENTL 表示身份标识的比特长度编码;
  • a , b a,b a,b 是曲线参数;
  • ( x G , y G ) (x_G,y_G) (xG,yG) 是基点坐标;
  • ( x A , y A ) (x_A,y_A) (xA,yA) 是签名者公钥坐标;
  • ∥ \| 表示按规定格式拼接。

然后对消息计算:

e = H v ( Z A ∥ M ) e=H_v(Z_A\mathbin\|M) e=Hv(ZAM)

这意味着 SM2 的签名摘要不仅取决于消息,还取决于用户身份、曲线参数和公钥。相同消息由不同用户签名时,摘要通常不同。


2.2 I D ID ID 不是可有可无的备注

实现 SM2 时必须明确:

  • 使用什么用户身份字符串;
  • 身份字符串采用什么字符编码;
  • ENTL ⁡ \operatorname{ENTL} ENTL 按比特还是字节计算;
  • 拼接时每个整数使用多少字节、什么字节序;
  • 签名者和验证者是否使用完全相同的身份与参数。

不能一方使用默认身份,另一方使用业务用户名;也不能把 UTF-8 字节长度直接当成标准要求的比特长度而不确认编码规则。 Z A Z_A ZA 不一致时,即使私钥和消息都正确,签名也会验证失败。


2.3 预计算 Z A Z_A ZA

在一个用户、曲线参数和身份固定的场景中, Z A Z_A ZA 可以预先计算并缓存,以减少每条消息的重复工作。但缓存必须与以下信息绑定:

  • 用户身份;
  • 公钥;
  • 曲线参数;
  • 使用的哈希算法和编码规则。

公钥或身份发生变化时,不能继续复用旧的 Z A Z_A ZA

在这里插入图片描述

图1: Z A Z_A ZA 输入拼接结构图解
Z A = H ( ENTL ⁡ ∥ I D A ∥ a ∥ b ∥ G ∥ P A ) Z_A = H(\operatorname{ENTL} \mathbin\| ID_A \mathbin\| a \mathbin\| b \mathbin\| G \mathbin\| P_A) ZA=H(ENTLIDAabGPA),按固定字节长度依次拼接:ENTL占2字节、 I D A ID_A IDA占16字节、曲线参数 a a a占32字节、 b b b占32字节、基点坐标 G ( x G , y G ) G(x_G,y_G) G(xG,yG)占64字节、公钥坐标 P A ( x A , y A ) P_A(x_A,y_A) PA(xA,yA)占64字节,整体作为哈希输入用于生成用户身份摘要。

在这里插入图片描述

图2:身份ID变更对 Z A Z_A ZA的影响图解
Z A Z_A ZA是哈希输出,雪崩效应显著:仅修改输入里的用户 I D A ID_A IDA,最终生成的32字节 Z A Z_A ZA摘要会整体发生改变,不会只局部变动;这也是SM2中签名、验签双方 Z A Z_A ZA必须完全一致,否则验签失败的核心原因。


三、SM2 数字签名流程

3.1 签名输入

签名者拥有私钥 d A d_A dA 和公钥 P A = [ d A ] G P_A=[d_A]G PA=[dA]G。对消息 M M M

  1. 计算用户身份摘要 Z A Z_A ZA
  2. 计算:
    e = H v ( Z A ∥ M ) e=H_v(Z_A\mathbin\|M) e=Hv(ZAM)
  3. 生成一次性随机数 k k k
  4. 通过点乘得到临时点;
  5. 计算签名 ( r , s ) (r,s) (r,s)

3.2 选择 nonce 并计算临时点

随机选择:

k ← $ { 1 , 2 , … , n − 1 } k\xleftarrow{\$}\{1,2,\ldots,n-1\} k$ {1,2,,n1}

计算:

( x 1 , y 1 ) = [ k ] G (x_1,y_1)=[k]G (x1,y1)=[k]G

然后令:

r = ( e + x 1 )   m o d   n r=(e+x_1)\bmod n r=(e+x1)modn

如果 r = 0 r=0 r=0,重新选择 k k k


3.3 计算 s s s

SM2 签名的核心公式为:

s = ( ( 1 + d A ) − 1 ( k − r d A ) )   m o d   n s=((1+d_A)^{-1}(k-rd_A))\bmod n s=((1+dA)1(krdA))modn

如果:

r + k = n r+k=n r+k=n

或:

s = 0 s=0 s=0

则按照标准重新选择 nonce 并重试。最终签名为:

σ = ( r , s ) \sigma=(r,s) σ=(r,s)

与 ECDSA 相比,SM2 的 s s s 公式结构不同,出现了 ( 1 + d A ) − 1 (1+d_A)^{-1} (1+dA)1,并且 r r r 使用了 e + x 1 e+x_1 e+x1 的形式。不能仅仅修改 ECDSA 的哈希算法或曲线参数,就把 ECDSA 实现当作 SM2。


3.4 SM2 签名验证

验证者首先检查:

1 ≤ r ≤ n − 1 , 1 ≤ s ≤ n − 1 1\le r\le n-1,\qquad1\le s\le n-1 1rn1,1sn1

然后根据同样的身份和公钥计算:

e = H v ( Z A ∥ M ) e=H_v(Z_A\mathbin\|M) e=Hv(ZAM)

计算:

t = ( r + s )   m o d   n t=(r+s)\bmod n t=(r+s)modn

t = 0 t=0 t=0,验证失败。

接着计算临时点:

( x 1 ′ , y 1 ′ ) = [ s ] G + [ t ] P A (x_1',y_1')=[s]G+[t]P_A (x1,y1)=[s]G+[t]PA

最后计算:

R = ( e + x 1 ′ )   m o d   n R=(e+x_1')\bmod n R=(e+x1)modn

当且仅当:

R = r R=r R=r

时接受签名。


3.5 验证公式的代数关系

由签名公式:

s ( 1 + d A ) ≡ k − r d A ( m o d n ) s(1+d_A)\equiv k-rd_A\pmod n s(1+dA)krdA(modn)

整理得到:

s + r ≡ k + ( r + s ) d A ( m o d n ) s+r\equiv k+(r+s)d_A\pmod n s+rk+(r+s)dA(modn)

令:

t = r + s ( m o d n ) t=r+s\pmod n t=r+s(modn)

则:

s + t d A ≡ k ( m o d n ) s+td_A\equiv k\pmod n s+tdAk(modn)

于是:

[ s ] G + [ t ] P A = [ s ] G + [ t ] [ d A ] G = [ s + t d A ] G = [ k ] G = ( x 1 , y 1 ) \begin{aligned} [s]G+[t]P_A &=[s]G+[t][d_A]G\\ &=[s+td_A]G\\ &=[k]G\\ &=(x_1,y_1) \end{aligned} [s]G+[t]PA=[s]G+[t][dA]G=[s+tdA]G=[k]G=(x1,y1)

所以验证者恢复了签名阶段的临时点,最终得到:

R = ( e + x 1 )   m o d   n = r R=(e+x_1)\bmod n=r R=(e+x1)modn=r

这解释了验证公式为什么成立。

在这里插入图片描述

图1:SM2签名从身份绑定摘要到验证点流程图解
SM2签名完整链路:先拼接 Z A ∥ M Z_A \mathbin\| M ZAM,经SM3哈希得到 e e e;随机数 k k k做标量乘法得到临时点 k G = ( x 1 , y 1 ) kG=(x_1,y_1) kG=(x1,y1),再计算出签名分量 ( r , s ) (r,s) (r,s);验签阶段通过公式 s G + t P A sG + tP_A sG+tPA还原临时点,完成校验。

在这里插入图片描述

图2:身份、消息与签名公式的绑定效果图解
标准SM2签名仅在【原ID+原消息】场景下验证通过;更换ID、篡改消息都会直接拒绝;如果实现时省略 Z A Z_A ZA,则无论原ID原消息、换ID、篡改消息全部验证失败,直观体现 Z A Z_A ZA对身份的强绑定作用。


四、SM2 签名中的 nonce 灾难

4.1 重复 nonce 的后果

SM2 的签名公式为:

s ( 1 + d A ) ≡ k − r d A ( m o d n ) s(1+d_A)\equiv k-rd_A\pmod n s(1+dA)krdA(modn)

如果两条不同消息错误地使用相同的 k k k,它们会得到相同的临时点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),但 不一定得到相同的 r r r。这是因为 SM2 的 r r r 还包含消息摘要:

r i = ( e i + x 1 )   m o d   n r_i=(e_i+x_1)\bmod n ri=(ei+x1)modn

设两次签名为 ( r 1 , s 1 ) (r_1,s_1) (r1,s1) ( r 2 , s 2 ) (r_2,s_2) (r2,s2)。由签名公式:

s i ( 1 + d A ) ≡ k − r i d A ( m o d n ) s_i(1+d_A)\equiv k-r_i d_A\pmod n si(1+dA)kridA(modn)

整理可得:

d A ≡ ( k − s i ) ( r i + s i ) − 1 ( m o d n ) d_A\equiv(k-s_i)(r_i+s_i)^{-1}\pmod n dA(ksi)(ri+si)1(modn)

A i = r i + s i A_i=r_i+s_i Ai=ri+si。当相关差值可逆时,两条签名可以联立恢复 nonce:

k ≡ ( s 1 A 2 − s 2 A 1 ) ( A 2 − A 1 ) − 1 ( m o d n ) k\equiv\bigl(s_1A_2-s_2A_1\bigr)(A_2-A_1)^{-1}\pmod n k(s1A2s2A1)(A2A1)1(modn)

然后代回:

d A ≡ ( k − s 1 ) A 1 − 1 ( m o d n ) d_A\equiv(k-s_1)A_1^{-1}\pmod n dA(ks1)A11(modn)

恢复结果应通过:

[ d A ] G = ? P A [d_A]G\stackrel{?}=P_A [dA]G=?PA

进行验证。由于 SM2 的 r r r 包含 e e e,不能直接照搬 ECDSA 中“寻找相同 r r r”的排查规则;但重复 nonce 仍然是严重漏洞,攻击者可以利用多条签名之间的代数关系恢复长期私钥。

因此,SM2 和 ECDSA 一样,签名 nonce 必须独立、不可预测且不能重复。不要因为 SM2 的签名公式不同,就认为随机数故障不再危险。


4.2 随机源和确定性生成

SM2 标准流程通常要求生成安全随机 nonce。工程实现可以根据适用标准和密码库支持,使用经过认证的随机源或规范允许的确定性生成机制,但必须保证:

  • 不同消息不会重复使用同一 nonce;
  • 随机状态不会因进程回滚、虚拟机快照或容器克隆而重复;
  • nonce 生成过程不泄露私钥;
  • 失败重试不会产生可预测序列。

对签名服务进行审计时,应重点检查随机源初始化、并发调用、异常重启和硬件随机数故障。

图1:相同nonce:临时点复用,但r仍受消息影响图解
若两次签名使用同一个随机数 k k k,临时点 k G kG kG完全一致;但由于消息不同, e e e值不同,最终算出的签名分量 r 1 r_1 r1 r 2 r_2 r2并不相等,不能直接通过r判断nonce复用。

在这里插入图片描述

图2:重复nonce导致的代数恢复结果图解
SM2签名一旦出现两次不同消息复用同一个nonce k k k的情况,可以通过两组 ( r 1 , s 1 ) (r_1,s_1) (r1,s1) ( r 2 , s 2 ) (r_2,s_2) (r2,s2)代数推导,先恢复出临时随机数 k k k,进一步直接算出签名者私钥 d A d_A dA,完整破解密钥。


五、SM2 公钥加密

5.1 加密目标

SM2 公钥加密的目标是让发送者使用接收者公钥加密消息,只有接收者持有私钥才能恢复消息。它是一个混合结构:

  1. 用临时点乘得到共享椭圆曲线点;
  2. 用 KDF 从共享点导出掩码;
  3. 用异或保护消息;
  4. 用哈希生成完整性校验值。

5.2 加密过程

接收者 Bob 的公钥为:

P B = [ d B ] G P_B=[d_B]G PB=[dB]G

发送者对消息 M M M 选择随机数:

k ← $ { 1 , 2 , … , n − 1 } k\xleftarrow{\$}\{1,2,\ldots,n-1\} k$ {1,2,,n1}

先计算临时公钥点:

C 1 = [ k ] G = ( x 1 , y 1 ) C_1=[k]G=(x_1,y_1) C1=[k]G=(x1,y1)

再计算共享点:

( x 2 , y 2 ) = [ k ] P B (x_2,y_2)=[k]P_B (x2,y2)=[k]PB

使用 KDF 导出与消息同长度的掩码:

t = KDF ⁡ ( x 2 ∥ y 2 , klen ⁡ ) t=\operatorname{KDF}(x_2\mathbin\|y_2,\operatorname{klen}) t=KDF(x2y2,klen)

如果 KDF 输出全为零,按标准重新选择 k k k。否则计算:

C 2 = M ⊕ t C_2=M\oplus t C2=Mt

再计算完整性摘要:

C 3 = H v ( x 2 ∥ M ∥ y 2 ) C_3=H_v(x_2\mathbin\|M\mathbin\|y_2) C3=Hv(x2My2)

密文由 C 1 , C 2 , C 3 C_1,C_2,C_3 C1,C2,C3 按标准规定的顺序编码。许多 SM2 实现使用:

C = C 1 ∥ C 3 ∥ C 2 C=C_1\mathbin\|C_3\mathbin\|C_2 C=C1C3C2

但历史实现和库接口可能接受其他顺序,不能只根据字段名称猜测编码。


5.3 解密过程

Bob 解析并验证 C 1 C_1 C1 后,使用私钥计算:

( x 2 , y 2 ) = [ d B ] C 1 (x_2,y_2)=[d_B]C_1 (x2,y2)=[dB]C1

因为:

[ d B ] C 1 = [ d B ] [ k ] G = [ k ] [ d B ] G = [ k ] P B [d_B]C_1=[d_B][k]G=[k][d_B]G=[k]P_B [dB]C1=[dB][k]G=[k][dB]G=[k]PB

所以 Bob 得到与发送者相同的共享点。接着:

  1. ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 重新计算 KDF 掩码 t t t
  2. 计算 M = C 2 ⊕ t M=C_2\oplus t M=C2t
  3. 重新计算:
    C 3 ′ = H v ( x 2 ∥ M ∥ y 2 ) C_3'=H_v(x_2\mathbin\|M\mathbin\|y_2) C3=Hv(x2My2)
  4. 比较 C 3 ′ C_3' C3 与密文中的 C 3 C_3 C3

只有完整性校验成功时才应返回明文。校验失败不能返回部分明文、详细错误差异或可被利用的中间结果。


5.4 为什么必须验证 C 3 C_3 C3

如果只做:

M = C 2 ⊕ KDF ⁡ ( x 2 ∥ y 2 ) M=C_2\oplus\operatorname{KDF}(x_2\mathbin\|y_2) M=C2KDF(x2y2)

而不验证 C 3 C_3 C3,攻击者就可以篡改 C 2 C_2 C2,导致解密结果中的对应比特发生翻转。 C 3 C_3 C3 把共享点和完整明文绑定起来,是 SM2 加密流程中的完整性保护部分。

在现代系统中,还要统一错误处理和时序,避免通过“密文格式错误”“KDF 全零”“摘要不一致”等不同错误响应构造解密预言机。

在这里插入图片描述

图1:SM2密文结构:C1 || C3 || C2图解
SM2标准密文由三部分拼接而成:C1为临时公钥点,占65字节;C3为完整性摘要,占32字节;C2为明文异或生成的密文,占37字节,整体顺序为C1 || C3 || C2

在这里插入图片描述

图2:为什么解密后必须验证C3图解
C3是完整性校验字段,不是冗余装饰。正常密文解密后校验C3匹配;若传输中C2比特被篡改,解密后重新计算的摘要会和C3不匹配,以此检测密文是否被修改。


六、SM2 密钥交换概览

6.1 为什么需要密钥交换协议

单独的静态 ECDH 只说明双方可以得到同一个共享点,但未必提供:

  • 双方身份绑定;
  • 临时密钥带来的前向安全性;
  • 双方确认对方确实计算出了同一个会话密钥;
  • 会话参数和密钥长度绑定。

SM2 密钥交换在长期密钥的基础上引入临时密钥、身份摘要和确认值,形成更完整的协议流程。


6.2 基本角色和参数

假设 Alice 和 Bob 各自拥有:

  • 长期私钥 d A , d B d_A,d_B dA,dB
  • 长期公钥 P A , P B P_A,P_B PA,PB
  • 用户身份 I D A , I D B ID_A,ID_B IDA,IDB
  • 临时私钥 r A , r B r_A,r_B rA,rB
  • 临时公钥 R A = [ r A ] G , R B = [ r B ] G R_A=[r_A]G,R_B=[r_B]G RA=[rA]G,RB=[rB]G

双方会根据身份、公钥和曲线参数计算各自的身份摘要。协议还会对临时点的坐标进行约化和组合,具体公式与角色、发起方/响应方顺序以及标准版本有关。


6.3 流程的高层结构

典型流程可以理解为:

  1. Alice 和 Bob 生成临时密钥对;
  2. 双方交换临时公钥;
  3. 各自使用长期私钥、对方长期公钥以及临时密钥计算共享点;
  4. 把共享点坐标、身份摘要、临时公钥和会话上下文输入 KDF;
  5. 生成确认哈希,双方互相发送并验证;
  6. 只有确认成功后才把派生结果作为会话密钥。

确认值的作用是让一方证明自己确实计算出了指定共享点,而不是只收到一个看起来合法的公钥。实际部署中,必须严格按照标准规定的角色顺序、字段拼接顺序和确认值计算方式实现。


6.4 密钥交换中的实现边界

  • 长期公钥和临时公钥都要验证;
  • 临时私钥必须来自安全随机源;
  • 不能忽略用户身份摘要;
  • KDF 输入必须包含标准要求的上下文;
  • 需要区分确认前的中间值和最终会话密钥;
  • 失败时不能把部分中间结果交给上层;
  • 双方应明确谁负责发起、谁负责响应以及密钥长度。

在这里插入图片描述

图1:SM2密钥交换不是一次简单的ECDH图解
SM2密钥交换同时使用双方长期密钥 ( d A / P A 、 d B / P B ) (d_A/P_A、d_B/P_B) (dA/PAdB/PB)与临时密钥 ( r A / R A 、 r B / R B ) (r_A/R_A、r_B/R_B) (rA/RArB/RB),结合身份ID_A、ID_B算出共享点,经KDF生成会话密钥 K K K,还会交互确认值S1/S2做校验;相比普通ECDH增加了身份绑定与双向确认,工程实现需要严格遵循标准的角色顺序和确认值公式。


七、SM2 与 ECDSA、ECDH 的区别

对比项ECDSASM2 签名ECDHSM2 密钥交换
核心目标数字签名数字签名共享密钥带身份确认的共享密钥
消息摘要通常 H ( M ) H(M) H(M) H ( Z A ∣ M ) H(Z_A|M) H(ZAM)不直接签名消息含身份和会话上下文
签名关系 s = k − 1 ( z + r d ) s=k^{-1}(z+rd) s=k1(z+rd) s = ( 1 + d ) − 1 ( k − r d ) s=(1+d)^{-1}(k-rd) s=(1+d)1(krd)不适用不适用
用户身份摘要通常不内置 Z A Z_A ZA 是重要组成需要额外认证身份绑定在协议中
加密 KDF不适用加密流程使用 KDF共享点后需 KDF共享点后需 KDF
完整性签名本身签名本身需要认证协议通过确认与协议组合

表格只比较核心结构。具体安全性还取决于曲线参数、编码、随机数、密钥派生、点验证和协议实现。

在这里插入图片描述


八、SM2 工程实现与 CTF 排查

8.1 签名验证失败时

按顺序检查:

  1. 曲线参数、基点和阶是否一致;
  2. 公钥是否满足曲线方程;
  3. 用户身份 I D ID ID 是否一致;
  4. ENTL ⁡ \operatorname{ENTL} ENTL 是否按规定计算;
  5. Z A Z_A ZA 的拼接字段和字节序是否一致;
  6. 消息是否使用了原始字节而不是错误的字符串表示;
  7. 签名是原始 r || s 还是 ASN.1 DER;
  8. r , s r,s r,s 是否在合法范围;
  9. 签名公式是否误用了 ECDSA;
  10. 验证时是否错误地把 p p p 用作 n n n

8.2 SM2 密文解密失败时

重点检查:

  • C1 是否包含正确前缀和坐标长度;
  • C1 是否在曲线上且不为无穷远点;
  • 密文顺序是 C1C3C2 还是其他约定;
  • KDF 输入是否为规定的坐标拼接;
  • KDF 长度是否按明文字节长度;
  • 是否正确处理 KDF 全零重试;
  • C3 的输入顺序是否为 x 2 ∥ M ∥ y 2 x_2\|M\|y_2 x2My2
  • 比较 C3 时是否使用恒定时间比较。

8.3 不要把小曲线结果当作 SM2 安全性

如果题目把 SM2 曲线替换成很小的 p p p,可以枚举点集、计算基点阶并暴力求离散对数。但这只是 CTF 的参数退化题,不能说明真实 SM2 可以被同样方法破解。

Notebook 中使用的教学参数是:

  • E ( F 17 ) : y 2 = x 3 + 2 x + 2 E(\mathbb F_{17}): y^2=x^3+2x+2 E(F17):y2=x3+2x+2
  • 基点 G = ( 5 , 1 ) G=(5,1) G=(5,1),阶为 19 19 19
  • 设教学私钥为 d = 13 d=13 d=13,得到 Q = [ d ] G = ( 16 , 4 ) Q=[d]G=(16,4) Q=[d]G=(16,4)
  • 通过枚举点乘结果恢复 d = 13 d=13 d=13

这组参数只用于展示“参数过小会让离散对数变成可枚举问题”,不代表真实 SM2 曲线存在同样弱点。

在这里插入图片描述


九、常见误区

9.1 SM2 就是 ECDSA 加上国密哈希

错误。SM2 签名有不同的身份摘要和签名公式,SM2 加密和密钥交换也有独立的流程。

9.2 Z A Z_A ZA 可以省略

错误。对 SM2 签名来说, Z A Z_A ZA 是把用户身份、公钥和曲线参数绑定到消息摘要的重要部分。省略它会得到另一个协议。

9.3 密文只需要 C1 和 C2

错误。C3 是完整性校验摘要。只解密 C2 而不验证 C3,会让攻击者可以篡改明文。

9.4 只要使用国密算法就自动安全

错误。算法名称不能替代安全实现。随机数、点验证、编码、错误处理、密钥存储和协议组合任何一环出错,都可能导致实际系统不安全。

9.5 SM2 的 nonce 可以固定

错误。SM2 签名的 nonce 与 ECDSA 一样必须安全生成。重复或泄露会导致签名关系泄露私钥。

在这里插入图片描述


十、总结

SM2 建立在 ECC 点群之上,但有自己的标准化协议结构:

  • 签名先计算用户身份摘要:
    Z A = H v ( ENTL ⁡ ∥ I D A ∥ a ∥ b ∥ G ∥ P A ) Z_A=H_v(\operatorname{ENTL}\|ID_A\|a\|b\|G\|P_A) ZA=Hv(ENTLIDAabGPA)
    再对 Z A ∥ M Z_A\|M ZAM 计算摘要,并使用:
    r = ( e + x 1 )   m o d   n r=(e+x_1)\bmod n r=(e+x1)modn
    s = ( 1 + d A ) − 1 ( k − r d A )   m o d   n s=(1+d_A)^{-1}(k-rd_A)\bmod n s=(1+dA)1(krdA)modn
  • 加密使用临时点、共享点、KDF、异或和 C3 摘要,通常形成 C1 || C3 || C2 的密文结构;
  • 密钥交换把长期密钥、临时密钥、身份摘要、KDF 和确认值组合起来,提供更完整的会话密钥协商。

理解 SM2 的关键不是死记几条公式,而是记住哪些信息必须绑定:用户身份、公钥、曲线参数、消息、临时随机数和协议上下文。 下一篇将从“公钥属于谁”这个问题出发,介绍数字证书、CA、证书链和 PKI 信任体系。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sagittarius_A*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值