土法炼钢兴趣小组的算法知识备份

【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读

文章导航

分类入口
cryptography
标签入口
#SM2#SM3#SM4#SM9#国密#ShangMi#Chinese-cryptography#ZUC#commercial-crypto

目录

国密算法体系:SM2/SM3/SM4/SM9 功能与参数概览

一、国密算法体系概览

先看一张图,把这一节的关键关系串起来。

graph TD
    A[国密算法族] --> B[SM2]
    A --> C[SM3]
    A --> D[SM4]
    B --> E[签名与加密]

“国密”(GM,GuoMi)是中国商用密码算法体系的通称。这一体系由国家密码管理局(SCA,State Cryptography Administration)主导设计与发布,旨在为国家关键信息基础设施、政务系统和金融行业提供自主可控的密码学基础。从上世纪九十年代末开始酝酿,到二十一世纪初陆续公开发布,国密算法走过了一段从内部使用到公开标准、再到国际认可的发展历程。

在历史背景方面,中国密码学研究起步并不晚——早在二十世纪六七十年代,为满足国防与外交通信需求,国内已有系统性的密码算法研究。然而,民用密码领域长期依赖国际算法(如 DES、AES、RSA、SHA 系列),核心算法的知识产权和潜在后门风险使得国家在战略层面产生了忧虑。2006 年前后,国家密码管理局先后公开了 SM2、SM3、SM4 等算法的技术规范,标志着中国商用密码进入了”公开透明、接受审查”的新阶段。

国密算法体系的标准化工作主要通过 GM/T 标准(密码行业标准)和 GB/T 标准(国家标准)两个层次进行。GM/T 标准由国家密码管理局发布,侧重于算法规范和接口定义;GB/T 标准则由国家标准化管理委员会发布,具有更广泛的法律效力。例如,SM4 分组密码最初以 GM/T 0002—2012 发布,后升格为 GB/T 32907—2016;SM3 杂凑算法对应 GB/T 32905—2016;SM2 椭圆曲线公钥密码算法对应 GB/T 32918 系列标准;SM9 标识密码对应 GB/T 38635 系列标准。

在国际标准化方面,国密算法近年取得了显著进展。SM2 数字签名算法于 2017 年被纳入 ISO/IEC 14888-3:2018 标准;SM3 杂凑算法被纳入 ISO/IEC 10118-3:2018 标准;SM9 标识密码算法的数字签名部分被纳入 ISO/IEC 14888-3:2018 标准。SM4 分组密码则在 2021 年正式成为 ISO/IEC 18033-3:2010/Amd 1:2021 的一部分。这些国际认可意味着国密算法在设计质量和安全强度上得到了国际密码学界的初步背书。

国密算法体系的整体架构可以概括如下:对称密码层面,SM4 提供 128 比特的分组加密能力,ZUC 提供流密码加密能力;杂凑算法层面,SM3 提供 256 比特的散列输出;公钥密码层面,SM2 基于椭圆曲线提供数字签名、密钥交换和公钥加密功能,SM9 基于双线性对(Bilinear Pairing)提供基于标识的密码功能。这一体系在功能上与国际主流算法体系(AES + SHA-256 + ECDSA/ECDH)形成了一一对应的替代关系。

下表以「族谱」的视角呈现国密标准体系的完整映射——从算法类型、核心功能到国家标准编号和典型应用场景:

算法 类型 核心功能 国家标准 国际标准 典型应用场景
SM2 公钥密码(椭圆曲线) 数字签名、密钥交换、公钥加密 GB/T 32918 系列 ISO/IEC 14888-3 PKI 体系、eID 电子身份、SSL/TLS
SM3 杂凑算法 消息摘要、完整性校验 GB/T 32905—2016 ISO/IEC 10118-3 数据完整性、区块链、数字签名预处理
SM4 对称分组密码 数据加密(128 比特) GB/T 32907—2016 ISO/IEC 18033-3/Amd1 VPN 加密、存储加密、WAPI 无线安全
SM9 标识密码(双线性对) 基于标识的签名/加密/密钥交换 GB/T 38635 系列 ISO/IEC 14888-3 IoT 设备认证、加密邮件、轻量级 PKI
ZUC 流密码 流式数据加密 GM/T 0001—2012 3GPP(EEA3/EIA3) 4G/5G 移动通信加密与完整性保护

个人观察: 围绕国密算法的讨论中,有一种常见的误读——将「密码主权」简单等同于政治行为。这种看法忽略了密码学独立性背后深刻的工程合理性。从纯技术角度看,对单一算法体系的全球依赖本身就是一种系统性风险:如果 AES 或 SHA-2 被发现存在结构性弱点(历史上 DES、MD5、SHA-1 都经历过这一过程),拥有独立且经过充分分析的替代算法意味着你有退路。SM3 和 SM4 的设计质量经受住了十余年的国际密码分析检验,SM2 所用的椭圆曲线参数也通过了标准的安全性审计。更务实地说,密码算法的多样性对整个生态是有益的——正如生物多样性增强了生态系统的鲁棒性,密码算法的多样性降低了「一把钥匙开所有锁」式的系统性灾难风险。当然,透明度是关键前提:SM4 的 S 盒设计准则未完全公开这一点确实是一个遗憾,值得持续推动改进。

二、SM4 分组密码

SM4 是中国第一个公开发布的商用分组密码算法,最初以 SMS4 之名应用于无线局域网安全标准 WAPI(Wireless Authentication and Privacy Infrastructure)。该算法的分组长度为 128 比特,密钥长度同样为 128 比特,共执行 32 轮变换。

SM4 的整体结构采用了非平衡 Feistel(Feistel-like)网络。在每一轮中,算法将 128 比特的状态分为四个 32 比特的字 X₀、X₁、X₂、X₃,轮函数 F 的计算方式为:

X_{i+4} = X_i ⊕ T(X_{i+1} ⊕ X_{i+2} ⊕ X_{i+3} ⊕ rk_i)

其中 rk_i 为第 i 轮的轮密钥,T 是由非线性变换 τ 和线性变换 L 复合而成的可逆变换。非线性变换 τ 由四个并行的 S 盒(S-box)查表操作构成,每个 S 盒是一个 8 比特到 8 比特的置换。线性变换 L 定义为:

L(B) = B ⊕ (B <<< 2) ⊕ (B <<< 10) ⊕ (B <<< 18) ⊕ (B <<< 24)

其中 <<< 表示循环左移操作。

SM4 的 S 盒设计是其安全性的核心要素之一。该 S 盒具有良好的差分均匀度和非线性度:其最大差分概率为 2⁻⁶,非线性度达到 112(理论最大值为 120)。与 AES 的 S 盒相比,SM4 的 S 盒在代数次数上略有不同——AES 的 S 盒基于 GF(2⁸) 上的乘法逆元构造,具有清晰的代数结构;SM4 的 S 盒则通过仿射变换与 GF(2⁸) 上的逆元复合而成,但其具体设计准则未完全公开,这曾引发学术界的一些讨论。

密钥扩展方面,SM4 使用系统参数 FK 和常量密钥 CK 对 128 比特主密钥进行扩展,生成 32 个 32 比特的轮密钥。密钥扩展的结构与加密轮函数类似,但使用了不同的线性变换 L’:

L'(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)

解密过程与加密过程完全相同,仅需将轮密钥的使用顺序颠倒即可。这种对称性是 Feistel 类结构的固有优势,简化了硬件实现。

SM4 支持多种工作模式,包括 ECB、CBC、CFB、OFB 和 CTR 等标准分组密码工作模式,以及 GCM 等认证加密模式。在实际应用中,GCM 模式因其同时提供机密性和完整性保护而备受青睐。

SM4 密码分析简史

SM4 自公开以来,经历了系统性的国际密码分析检验,攻击结果的演进清晰地勾勒出其安全余量的轮廓:

差分分析(Differential Cryptanalysis)方面,2010 年 Su 等人首次给出了对 22 轮 SM4 的差分攻击,数据复杂度约 2¹¹⁷ 个选择明文、时间复杂度约 2¹²⁵·⁷。2012 年同一团队将攻击延伸至 23 轮,数据复杂度 2¹²⁰、时间复杂度 2¹²⁸(接近穷举搜索)。这一结果至今仍是差分路径下的最优记录。

线性分析(Linear Cryptanalysis)方面,Etrog 和 Robshaw 于 2008 年系统评估了 SM4 的线性特性,指出其最佳线性逼近偏差使得全轮线性攻击不可行。后续研究采用多线性分析和零相关线性分析(Zero-Correlation Linear Cryptanalysis)等进阶技术,Liu 等人在 2014 年将零相关攻击推进至 14 轮。

不可能差分分析(Impossible Differential)方面,学者们构造了覆盖 16 至 18 轮的不可能差分路径,在此基础上对 23 轮 SM4 发起密钥恢复攻击,时间复杂度约 2¹²⁶·⁷。这类攻击在轮数突破上与经典差分分析持平,但所需数据量更为可控。

相关密钥攻击(Related-Key Attack)方面,SM4 密钥扩展算法使用了与轮函数相似但不同的线性变换 L’,且引入了 32 个各不相同的常量密钥 CK,使得相关密钥差分的构造极为困难。目前已公开的最优相关密钥攻击覆盖轮数不超过 23 轮,表明密钥调度方案的设计是稳健的。

综合来看,SM4 完整 32 轮与最优攻击之间存在 8 至 9 轮的安全余量。

SM4 与 AES 安全余量对比

SM4 与 AES 的比较是一个常见话题。下表从结构设计、安全分析和实现效率三个维度进行对照:

对比维度 SM4 AES-128
整体结构 广义 Feistel 网络(非平衡) 代换-置换网络(SPN)
分组长度 128 比特 128 比特
密钥长度 128 比特(仅一种) 128/192/256 比特
轮数 32 10(128 比特密钥)
S 盒构造 仿射变换 + GF(2⁸) 逆元,设计准则未完全公开 GF(2⁸) 逆元 + 仿射变换,设计准则完全公开
S 盒非线性度 112 112
S 盒最大差分概率 2⁻⁶ 2⁻⁶
最优已知攻击轮数 23—24 轮(差分/不可能差分) 7 轮(biclique,AES-128)
安全余量(轮数) 8—9 轮(约 25%—28%) 3 轮(约 30%)
安全余量(百分比)解读 绝对轮数余量更大,但每轮扩散速度较 SPN 慢 百分比略高,且 SPN 每轮提供更完整的扩散
硬件加速指令 海光/鲲鹏/飞腾等国产 CPU;ARM v8.4 可选 SM4 指令 Intel/AMD AES-NI;ARM AES 指令;几乎所有现代 CPU
纯软件吞吐量(参考) 约 200—400 MB/s(无硬件加速,64 位平台) 约 4—6 GB/s(AES-NI),约 600 MB/s(纯软件)
硬件面积(ASIC 参考) 轮函数简单,32 轮可流水线化,面积适中 10 轮 SPN,面积较小,流水线效率高

两者在 S 盒的代数参数(非线性度和最大差分概率)上完全相同,这并非巧合——SM4 的 S 盒同样基于 GF(2⁸) 上的乘法逆元构造,只是叠加了不同的仿射层。关键区别在于整体结构:SPN 每轮对全部 128 比特进行非线性变换和扩散,而广义 Feistel 每轮仅变换 32 比特然后通过异或传播。这意味着 SM4 需要更多轮数才能达到同等的扩散效果——32 轮恰好是这一设计折中的体现。从安全余量的角度看,两者都保持了足够的安全边际,SM4 的绝对轮数余量(8—9 轮)甚至略优于 AES-128 的 3 轮余量。

三、SM3 杂凑算法

SM3 是中国商用密码体系中的杂凑算法(Hash Function),输出长度为 256 比特,适用于数字签名、消息认证码(MAC)和随机数生成等场景。该算法由王小云院士团队设计,于 2010 年正式公开发布。

SM3 采用了经典的 Merkle-Damgård 迭代结构。输入消息首先进行填充——在消息末尾添加比特”1”,然后添加若干比特”0”,最后附加一个 64 比特的消息长度字段,使得填充后的消息长度为 512 比特的整数倍。填充后的消息被分为若干个 512 比特的分组,逐一输入压缩函数进行处理。

SM3 的压缩函数(Compression Function)是算法的核心。它接收一个 256 比特的链接值(Chaining Value)和一个 512 比特的消息分组作为输入,输出一个 256 比特的新链接值。压缩函数内部执行 64 轮迭代,分为两个阶段:前 16 轮使用常量 Tj = 0x79CC4519,后 48 轮使用常量 Tj = 0x7A879D8A。

消息扩展(Message Expansion)是 SM3 的一个重要环节。512 比特的消息分组首先被拆分为 16 个 32 比特的字 W₀ 至 W₁₅,然后通过以下递推公式扩展为 68 个字:

W_j = P₁(W_{j-16} ⊕ W_{j-9} ⊕ (W_{j-3} <<< 15)) ⊕ (W_{j-13} <<< 7) ⊕ W_{j-6}

其中 P₁(X) = X ⊕ (X <<< 15) ⊕ (X <<< 23) 是置换函数。此外,还需计算 W’j = W_j ⊕ W{j+4}(j = 0, 1, …, 63),用于压缩函数的迭代运算。

压缩函数每一轮的运算涉及两个布尔函数 FF 和 GG。在前 16 轮中:

FF_j(X, Y, Z) = X ⊕ Y ⊕ Z
GG_j(X, Y, Z) = X ⊕ Y ⊕ Z

在后 48 轮中:

FF_j(X, Y, Z) = (X ∧ Y) ∨ (X ∧ Z) ∨ (Y ∧ Z)
GG_j(X, Y, Z) = (X ∧ Y) ∨ (¬X ∧ Z)

与 SHA-256 的比较,两者在结构上有诸多相似之处:均采用 Merkle-Damgård 结构,输出长度均为 256 比特,消息分组长度均为 512 比特。但在细节设计上存在值得深究的差异。SHA-256 的消息扩展使用两次 σ 函数的异或组合,扩展后的字之间依赖关系相对稀疏;SM3 的消息扩展引入了置换函数 P₁ 和循环移位的组合,使得每个扩展字依赖于更多前驱字,扩散速度更快。这一设计选择直接影响了差分路径的构造难度——攻击者在 SM3 的消息扩展中寻找高概率差分路径时,受到的约束条件比 SHA-256 更多。在布尔函数方面,SHA-256 在全部 64 轮中使用 Ch 和 Maj 两个布尔函数(类型不变,仅轮常量不同),而 SM3 在第 0—15 轮和第 16—63 轮分别使用不同类型的布尔函数 FF 和 GG,这种分段设计增加了跨阶段差分传播的不连续性。此外,SM3 的步函数中包含置换 P₀(三路循环移位异或),而 SHA-256 使用 Σ 函数(同为三路循环移位异或但参数不同)。两者的设计哲学一脉相承,但具体参数的选择反映了各自对扩散与非线性平衡点的不同判断。

SM3 密码分析简史

SM3 自 2010 年公开以来,国内外学者对其进行了大量密码分析研究,攻击技术的演进与 SHA-256 的分析路径高度平行:

碰撞攻击方面,2011 年 Mendel 等人率先给出了对 20 步简化 SM3 的碰撞攻击。2012 年,Zou 等人将碰撞攻击推进至 24 步。此后,研究者通过改进差分路径搜索策略和消息修改技术,将碰撞攻击的覆盖范围逐步扩展——目前已知的最优结果是对 32 步 SM3 的 free-start 碰撞攻击(即不固定初始链接值的碰撞),由 Mendel 等人在 2013 年给出。需要注意的是,free-start 碰撞与标准碰撞在实际威胁上存在本质差别:标准碰撞攻击要求使用算法规定的固定初始值,而 free-start 碰撞允许攻击者自由选择初始值,后者在实际应用场景中几乎无法被利用。在标准模型下,SM3 的碰撞攻击目前仅覆盖约 24—28 步,完整 64 步的碰撞抗性未受到实质性威胁。

原像攻击方面,Bai 等人在 2012 年给出了对 30 步 SM3 的伪原像攻击(Pseudo-Preimage Attack),复杂度约 2²⁵¹。Wang 和 Shen 在 2014 年改进了中间相遇(Meet-in-the-Middle)技术,将伪原像攻击推进至 33 步。完整 64 步的原像安全性余量同样充裕。

与 SHA-256 分析结果对比:SHA-256 的最优碰撞攻击覆盖 31 步(实际碰撞)和 38 步(free-start 碰撞),而 SM3 的对应结果分别约为 24—28 步和 32 步。表面上看,SM3 的被攻击轮数更少,但考虑到 SHA-256 的研究历史更长(2001 年公开,比 SM3 早近十年)、投入的分析资源更多,两者在密码分析抵抗力上处于可比水平。SM3 更复杂的消息扩展确实在实践中增加了差分路径搜索的计算成本。

以下是使用 Python 计算 SM3 杂凑值的示例代码:

#!/usr/bin/env python3
"""SM3 杂凑算法演示(使用 gmssl 库)"""

# pip install gmssl
from gmssl import sm3, func

def sm3_hash(message: str) -> str:
    """计算字符串的 SM3 杂凑值,返回十六进制摘要。"""
    msg_bytes = message.encode("utf-8")
    # func.bytes_to_list 将 bytes 转为整数列表
    msg_list = func.bytes_to_list(msg_bytes)
    digest = sm3.sm3_hash(msg_list)
    return digest

def sm3_hash_manual(data: bytes) -> str:
    """
    SM3 杂凑算法的简化纯 Python 实现。
    仅用于教学演示,生产环境请使用经过审计的密码库。
    """
    # ---------- 常量 ----------
    IV = [
        0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
        0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E,
    ]

    T = [0x79CC4519] * 16 + [0x7A879D8A] * 48

    def rotl(x, n):
        """32 比特循环左移"""
        return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

    def p0(x):
        return x ^ rotl(x, 9) ^ rotl(x, 17)

    def p1(x):
        return x ^ rotl(x, 15) ^ rotl(x, 23)

    def ff(j, x, y, z):
        if j < 16:
            return x ^ y ^ z
        return (x & y) | (x & z) | (y & z)

    def gg(j, x, y, z):
        if j < 16:
            return x ^ y ^ z
        return (x & y) | (~x & z & 0xFFFFFFFF)

    # ---------- 填充 ----------
    msg = bytearray(data)
    bit_len = len(msg) * 8
    msg.append(0x80)
    while (len(msg) * 8) % 512 != 448:
        msg.append(0x00)
    msg += bit_len.to_bytes(8, "big")

    # ---------- 分组迭代 ----------
    V = list(IV)
    for i in range(0, len(msg), 64):
        block = msg[i : i + 64]
        W = [int.from_bytes(block[j * 4 : j * 4 + 4], "big") for j in range(16)]
        for j in range(16, 68):
            tmp = p1(W[j - 16] ^ W[j - 9] ^ rotl(W[j - 3], 15))
            W.append((tmp ^ rotl(W[j - 13], 7) ^ W[j - 6]) & 0xFFFFFFFF)
        Wp = [(W[j] ^ W[j + 4]) & 0xFFFFFFFF for j in range(64)]

        A, B, C, D, E, F, G, H = V

        for j in range(64):
            SS1 = rotl((rotl(A, 12) + E + rotl(T[j], j % 32)) & 0xFFFFFFFF, 7)
            SS2 = SS1 ^ rotl(A, 12)
            TT1 = (ff(j, A, B, C) + D + SS2 + Wp[j]) & 0xFFFFFFFF
            TT2 = (gg(j, E, F, G) + H + SS1 + W[j]) & 0xFFFFFFFF
            D = C
            C = rotl(B, 9)
            B = A
            A = TT1
            H = G
            G = rotl(F, 19)
            F = E
            E = p0(TT2)

        V = [(v ^ x) & 0xFFFFFFFF for v, x in zip(V, [A, B, C, D, E, F, G, H])]

    return "".join(f"{w:08x}" for w in V)

if __name__ == "__main__":
    # 标准测试向量:对 "abc" 计算 SM3
    test_msg = "abc"
    expected = "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"

    result = sm3_hash_manual(test_msg.encode("utf-8"))
    print(f"消息: \"{test_msg}\"")
    print(f"SM3: {result}")
    print(f"校验: {'通过' if result == expected else '失败'}")

    # 使用 gmssl 库验证
    try:
        lib_result = sm3_hash(test_msg)
        print(f"gmssl 库结果: {lib_result}")
    except ImportError:
        print("(gmssl 库未安装,跳过库验证)")

四、SM2 椭圆曲线算法

SM2 是中国商用密码体系中的椭圆曲线公钥密码算法(Elliptic Curve Cryptography,ECC),涵盖数字签名、密钥交换和公钥加密三项核心功能。该算法基于素数域上的椭圆曲线离散对数问题(ECDLP,Elliptic Curve Discrete Logarithm Problem),在 256 比特的密钥长度下提供约 128 比特的安全强度。

SM2 推荐使用的椭圆曲线参数集通常被称为 sm2p256v1,其定义在 256 比特的素数域 GF(p) 上,曲线方程为标准的 Weierstrass 形式:

y² = x³ + ax + b (mod p)

具体参数如下:

这条曲线的选取经过了严格的安全性审查。余因子 h = 1,意味着曲线上所有点都属于由基点 G 生成的循环群,避免了小子群攻击的风险。此外,该曲线的判别式不为零,embedding degree 足够大,能够抵抗 MOV 攻击(将 ECDLP 归约到有限域上的离散对数问题)。

密钥生成过程非常直接:随机选取一个整数 d ∈ [1, n-2] 作为私钥,计算 P = d·G 作为公钥。这里的标量乘法 d·G 是将基点 G 与自身相加 d 次的结果,在已知 P 和 G 的情况下求解 d 在计算上是不可行的——这正是椭圆曲线密码学的安全基础。

SM2 公钥加密方案采用了类似于 ECIES(Elliptic Curve Integrated Encryption Scheme)的混合加密模式。加密过程如下:

  1. 随机选取整数 k ∈ [1, n-1],计算 C₁ = k·G(即临时公钥);
  2. 计算共享点 S = k·P_B(P_B 为接收方公钥),若 S 为无穷远点则返回错误;
  3. 利用密钥派生函数 KDF(Key Derivation Function)从共享点的坐标派生对称密钥 t = KDF(x₂ ‖ y₂, klen);
  4. 计算密文 C₂ = M ⊕ t(M 为明文);
  5. 计算杂凑值 C₃ = SM3(x₂ ‖ M ‖ y₂),作为完整性校验码;
  6. 输出密文 C = C₁ ‖ C₃ ‖ C₂(新标准格式)或 C = C₁ ‖ C₂ ‖ C₃(旧标准格式)。

解密过程则为加密的逆操作:接收方使用私钥 d_B 计算共享点 S = d_B·C₁ = d_B·k·G = k·P_B,然后依次恢复密钥、明文并验证杂凑值。

SM2 加密方案中一个值得注意的设计特点是 KDF 的使用方式。与 RSA-OAEP 等方案不同,SM2 的 KDF 直接从椭圆曲线点的坐标推导密钥流,然后与明文进行异或操作。这意味着 SM2 加密在本质上是一种流密码式的加密,密钥流的长度需要与明文长度匹配。这一设计使得 SM2 加密可以处理任意长度的明文,但也意味着对于较长的消息,KDF 需要产生足够长的输出。

SM2 加密方案中引入了 ZA 值的概念,这是 SM2 区别于其他椭圆曲线方案的一个重要特征。ZA 是用户标识信息(包括用户 ID、椭圆曲线参数和用户公钥)经 SM3 杂凑后得到的 256 比特哈希值。在签名和密钥交换协议中,ZA 作为前缀参与运算,有效地将用户身份绑定到密码学操作中,增强了协议的安全性。

以下示例代码展示了 SM2 密钥生成的基本流程:

#!/usr/bin/env python3
"""SM2 密钥生成与签名验签演示"""

import secrets

# SM2 推荐曲线参数
SM2_P  = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
SM2_A  = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
SM2_B  = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
SM2_N  = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
SM2_GX = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
SM2_GY = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0

INF = (None, None)  # 无穷远点

def mod_inv(a: int, m: int) -> int:
    """模逆元(扩展欧几里得算法)"""
    if a < 0:
        a = a % m
    g, x, _ = _extended_gcd(a, m)
    if g != 1:
        raise ValueError("模逆元不存在")
    return x % m

def _extended_gcd(a, b):
    if a == 0:
        return b, 0, 1
    g, x, y = _extended_gcd(b % a, a)
    return g, y - (b // a) * x, x

def point_add(P, Q, p=SM2_P, a=SM2_A):
    """椭圆曲线点加法"""
    if P == INF:
        return Q
    if Q == INF:
        return P
    x1, y1 = P
    x2, y2 = Q
    if x1 == x2:
        if (y1 + y2) % p == 0:
            return INF
        # 倍点公式
        lam = (3 * x1 * x1 + a) * mod_inv(2 * y1, p) % p
    else:
        lam = (y2 - y1) * mod_inv(x2 - x1, p) % p
    x3 = (lam * lam - x1 - x2) % p
    y3 = (lam * (x1 - x3) - y1) % p
    return (x3, y3)

def scalar_mult(k: int, P, p=SM2_P, a=SM2_A):
    """标量乘法(双倍加法)"""
    result = INF
    addend = P
    while k > 0:
        if k & 1:
            result = point_add(result, addend, p, a)
        addend = point_add(addend, addend, p, a)
        k >>= 1
    return result

def sm2_keygen():
    """生成 SM2 密钥对"""
    G = (SM2_GX, SM2_GY)
    # 私钥: 随机整数 d ∈ [1, n-2]
    d = secrets.randbelow(SM2_N - 2) + 1
    # 公钥: P = d·G
    P = scalar_mult(d, G)
    return d, P

def format_hex(value: int, width: int = 64) -> str:
    """将整数格式化为定长十六进制字符串"""
    return f"{value:0{width}x}"

if __name__ == "__main__":
    print("=== SM2 密钥生成演示 ===\n")
    private_key, public_key = sm2_keygen()
    print(f"私钥 d  = {format_hex(private_key)}")
    print(f"公钥 Px = {format_hex(public_key[0])}")
    print(f"公钥 Py = {format_hex(public_key[1])}")

    # 验证公钥在曲线上
    x, y = public_key
    lhs = (y * y) % SM2_P
    rhs = (x * x * x + SM2_A * x + SM2_B) % SM2_P
    assert lhs == rhs, "公钥不在曲线上!"
    print("\n公钥验证:点在 SM2 推荐曲线上 通过")

    # 验证 n·G = O(无穷远点)
    G = (SM2_GX, SM2_GY)
    O = scalar_mult(SM2_N, G)
    assert O == INF, "n·G 应为无穷远点"
    print("阶验证:n·G = O 通过")

五、SM2 签名与密钥交换

SM2 数字签名算法是国密体系中使用最广泛的公钥密码功能,其在协议设计上与 ECDSA(Elliptic Curve Digital Signature Algorithm)有相似之处,但在关键步骤上存在显著差异。

SM2 签名算法的核心流程如下。设签名者的私钥为 d_A,公钥为 P_A = d_A·G。对消息 M 签名时:

  1. 计算 ZA = SM3(ENTLA ‖ IDA ‖ a ‖ b ‖ Gx ‖ Gy ‖ xA ‖ yA),其中 IDA 为用户标识(默认为十六进制字符串 “1234567812345678”),ENTLA 为 IDA 的比特长度;
  2. 计算消息杂凑 e = SM3(ZA ‖ M),将 e 转换为整数;
  3. 随机选取 k ∈ [1, n-1],计算椭圆曲线点 (x₁, y₁) = k·G;
  4. 计算 r = (e + x₁) mod n,若 r = 0 或 r + k = n,则返回步骤 3 重新选取 k;
  5. 计算 s = ((1 + d_A)⁻¹ · (k - r·d_A)) mod n,若 s = 0,则返回步骤 3;
  6. 输出签名 (r, s)。

验签过程如下:

  1. 同样计算 ZA 和 e;
  2. 计算 t = (r + s) mod n,若 t = 0 则签名无效;
  3. 计算椭圆曲线点 (x₁’, y₁’) = s·G + t·P_A;
  4. 验证 (e + x₁’) mod n 是否等于 r。

与 ECDSA 相比,SM2 签名的主要差异体现在以下几点。第一,SM2 在签名过程中引入了用户标识 ZA,将签名者的身份信息嵌入到签名计算中,这增强了抗身份伪造的能力。ECDSA 不包含类似机制,签名仅与消息和密钥相关。第二,SM2 签名中 r 的计算方式为 r = (e + x₁) mod n,而 ECDSA 中 r = x₁ mod n,即 SM2 将消息杂凑值直接参与到 r 的计算中。第三,SM2 中 s 的计算公式涉及 (1 + d_A)⁻¹,而 ECDSA 中为 k⁻¹,这使得两者的代数结构有所不同。

SM2 签名算法对随机数 k 的安全性要求极为严格,与 ECDSA 相同——若两次签名使用了相同的 k 值,攻击者可以通过两个签名方程联立求解出私钥 d_A。因此,在实际实现中通常采用 RFC 6979 类似的确定性随机数生成方案(Deterministic k Generation),或确保使用高质量的密码学安全随机数生成器。

SM2 密钥交换协议(Key Exchange Protocol)允许通信双方在不安全的信道上协商出共享密钥。该协议基于椭圆曲线 Diffie-Hellman(ECDH)的思想,但增加了额外的安全措施。协议流程概述如下:

  1. 双方各自生成临时密钥对:发起方 A 生成 (r_A, R_A = r_A·G),响应方 B 生成 (r_B, R_B = r_B·G);
  2. 双方交换临时公钥 R_A 和 R_B;
  3. 双方各自利用己方的长期私钥、临时私钥以及对方的长期公钥和临时公钥,通过一个特定的计算公式得到共享秘密点;
  4. 利用 KDF 从共享秘密点派生出对称密钥。

SM2 密钥交换协议的一个重要特点是它提供了密钥确认(Key Confirmation)步骤——双方在密钥协商完成后还可以交换各自计算的杂凑值来验证对方确实持有正确的密钥。这使得协议能够抵抗中间人攻击,前提是双方的长期公钥已经通过可信渠道(如 CA 证书)进行了绑定。

六、SM9 标识密码

SM9 是中国商用密码体系中的标识密码(Identity-Based Cryptography,IBC)算法,由密码学家程朝辉等人设计。标识密码的核心思想是:用户的公钥直接由其标识信息(如电子邮件地址、手机号码或身份证号)派生,无需传统的公钥证书基础设施(PKI)。这一思想最早由 Shamir 于 1984 年提出,但直到 2001 年 Boneh 和 Franklin 基于双线性对给出了第一个实用的标识加密方案(IBE),标识密码才真正进入工程化阶段。SM9 正是在这一学术脉络上发展而来的中国方案。

双线性对与数学基础

SM9 基于双线性对(Bilinear Pairing)构建,使用 BN 曲线(Barreto-Naehrig Curve)作为其底层数学结构。双线性对是一个映射 e: G₁ × G₂ → G_T,满足三个性质:双线性性(e(aP, bQ) = e(P, Q)^{ab})、非退化性(存在 P, Q 使得 e(P, Q) ≠ 1)和可计算性(存在高效算法计算 e)。其中 G₁ 和 G₂ 是椭圆曲线上的两个阶为素数 N 的循环群,G_T 是有限域扩张上的乘法群。

SM9 采用 Type-3 配对(即 G₁ ≠ G₂ 且不存在高效的从 G₂ 到 G₁ 的同态映射),这是目前最高效的配对类型。BN 曲线的嵌入度(embedding degree)为 12,这意味着 G_T 嵌入在 GF(p¹²) 中。SM9 推荐的 256 比特 BN 曲线参数 t、p、N 经过精心选取,使得配对运算在该参数集上可以高效执行。

密钥生成中心(KGC)信任模型

标识密码体系的核心组件是密钥生成中心(KGC,Key Generation Center)。KGC 在系统建立阶段选择主私钥 s ∈ [1, N-1],计算主公钥并公布系统参数。此后,KGC 根据用户的标识信息为其生成对应的私钥。

这一架构带来的根本问题是密钥托管(Key Escrow):KGC 持有主私钥 s,理论上可以计算出任意用户的私钥,从而解密任何用户的消息或伪造任何用户的签名。这不是 SM9 的设计缺陷,而是所有标识密码方案的固有特征——Boneh-Franklin IBE、Sakai-Kasahara IBE 以及 SM9 概莫能外。

应对密钥托管问题的工程策略包括:

签名、加密与密钥交换

SM9 定义了三种密码功能,分别对应 GB/T 38635 的不同部分:

SM9 标识签名(IBS):签名者使用 KGC 为其签发的标识私钥 d_A ∈ G₁ 对消息进行签名。签名过程涉及配对运算 g = e(P₁, P_pub),其中 P₁ 为 G₁ 的生成元,P_pub 为主公钥。签名者选取随机数 r,计算 w = g^r,再计算 h = H₂(M ‖ w) 和 l = (r - h) mod N,最终计算 S = l · d_A。签名输出为 (h, S)。验签者仅需签名者的标识 ID_A 和系统公参即可验证——计算 h₁ = H₁(ID_A),恢复用户公钥 P = h₁ · P₂ + P_pub,验证配对等式 e(S, P) · g^h 是否等于 g^r。

SM9 标识加密(IBE):发送方仅需接收方的标识 ID_B 和系统公参即可加密。具体流程为:计算 Q_B = H₁(ID_B) · P₁,选取随机数 r,计算 C₁ = r · Q_B 作为密文第一分量。然后计算配对值 g = e(P_pub, P₂),由 w = g^r 通过 KDF 派生对称密钥 K₁ ‖ K₂,用 K₁ 加密消息得到 C₂,用 K₂ 计算消息认证码 C₃。接收方使用 KGC 签发的私钥 d_B ∈ G₂ 解密——计算 w’ = e(C₁, d_B),由此恢复对称密钥并解密。

SM9 密钥交换(IBKE):双方利用各自的标识私钥和对方的标识信息,通过交换临时参数协商出共享密钥。协议提供可选的密钥确认步骤,可抵抗中间人攻击。

与国际标识密码方案的对比

对比维度 SM9 Boneh-Franklin IBE Sakai-Kasahara IBE
配对类型 Type-3(最优 Ate 配对) Type-1(对称配对) Type-1/Type-3
底层曲线 256 位 BN 曲线 超奇异曲线或 BN 曲线 BN 曲线
标识映射 H₁ 映射到群元素 H₁ 映射到群元素 H₁ 映射到标量,乘以生成元
功能覆盖 签名 + 加密 + 密钥交换 仅加密(签名/交换需另行构造) 仅加密
国际标准 ISO/IEC 14888-3(签名部分) IEEE P1363.3 RFC 6508(MIKEY-SAKKE)
安全归约 BDH/DBDH BDH(随机谕言模型) Gap-BDH
密钥托管 有(KGC 持有主私钥)

SM9 相较于 Boneh-Franklin 方案的一个显著优势是功能完整性:SM9 在同一数学框架下定义了签名、加密和密钥交换三种原语,而 Boneh-Franklin 仅定义了加密功能。Sakai-Kasahara IBE 被英国 GCHQ 的 MIKEY-SAKKE 协议采用(RFC 6508),主要用于政府通信加密,其应用场景与 SM9 有相似之处。

部署场景与局限性

SM9 最适合以下场景:

SM9 的局限性同样需要正视。除密钥托管问题外,密钥吊销是另一个实际挑战——标识密码中用户的”公钥”是其身份信息,无法像传统 PKI 那样通过吊销证书来作废公钥。常见的解决方案是在标识中嵌入有效期(如 “‖2026Q1”),但这要求用户定期向 KGC 申请新的私钥。此外,SM9 所依赖的 BN 曲线的安全性评估也值得关注——2016 年 Kim 和 Barbulescu 改进了数域筛法在有限域扩张上的复杂度估计,使得 BN-256 曲线的实际安全强度从预期的 128 比特降至约 100—110 比特。尽管目前的共识认为这一安全强度在实际应用中仍然足够,但长远来看可能需要迁移至更高安全强度的曲线(如 BLS-381 或 BN-462),这将带来性能开销的增加。

七、ZUC 流密码

ZUC(祖冲之密码)是中国提出并被 3GPP(Third Generation Partnership Project)采纳的流密码算法,用于 4G LTE 网络中的数据加密(128-EEA3)和完整性保护(128-EIA3)。该算法以中国古代数学家祖冲之命名,体现了中国密码学界对自主创新的追求。

ZUC 的内部结构由三个核心部件组成:线性反馈移位寄存器(LFSR,Linear Feedback Shift Register)、比特重组层(BR,Bit Reorganization)和有限状态机(FSM,Finite State Machine)。

LFSR 是 ZUC 的状态存储核心,由 16 个 31 比特的寄存器单元 s₀ 至 s₁₅ 组成。LFSR 工作在素数域 GF(2³¹ - 1) 上——选择梅森素数 2³¹ - 1 作为模数使得取模运算可以高效实现。LFSR 的反馈多项式经过精心设计,确保生成序列的周期达到最大值 (2³¹ - 1)¹⁶ - 1。

比特重组层从 LFSR 的 16 个寄存器单元中选取特定的比特段,组合成四个 32 比特的字 X₀、X₁、X₂、X₃,供有限状态机使用。这一层的设计目标是打破 LFSR 输出的线性关系,增加密钥流的非线性度。

有限状态机由两个 32 比特的状态变量 R₁ 和 R₂ 组成,并包含两个 S 盒(S₀ 和 S₁)。FSM 的每一步接收来自比特重组层的输入,经过非线性变换后输出一个 32 比特的密钥流字。FSM 的非线性变换包括 S 盒查表、模加运算和线性变换,这些操作的组合为密钥流提供了良好的统计特性和抗密码分析能力。

ZUC 的初始化过程接收 128 比特的密钥和 128 比特的初始向量(IV),通过 32 轮初始化迭代建立内部状态。初始化完成后,每次调用密钥流生成函数可输出一个 32 比特的密钥流字。

128-EEA3 是基于 ZUC 的加密算法,工作方式类似于流密码的标准用法——将密钥流与明文逐比特异或得到密文。128-EIA3 是基于 ZUC 的消息认证码算法,利用密钥流和通用杂凑函数构造消息认证码,提供完整性保护。

ZUC 在 3GPP 标准中的地位与 SNOW 3G(128-EEA1/128-EIA1)和 AES(128-EEA2/128-EIA2)并列,是三套可选的安全算法之一。ZUC 的加入使得 4G/5G 网络的安全机制拥有了算法多样性,降低了单一算法被攻破带来的系统性风险。

八、国密合规与部署

随着《中华人民共和国密码法》于 2020 年 1 月 1 日正式实施,国密算法的合规部署从行业倡议上升为法律要求。密码法将密码分为核心密码、普通密码和商用密码三类,其中商用密码用于保护不属于国家秘密的信息,由市场主体依法自主使用。

在金融领域,中国人民银行早在 2013 年就发布了《关于做好商用密码管理工作的通知》,要求金融行业在新建和改造的信息系统中优先采用国密算法。随后,银联、各商业银行和支付机构逐步推进了国密改造。目前,银联卡芯片(IC 卡)、网银系统、手机银行和支付终端等均已广泛部署 SM2/SM3/SM4 算法。CFCA(中国金融认证中心,China Financial Certification Authority)作为金融行业的核心 CA 机构,负责签发基于 SM2 算法的数字证书,支持 SSL/TLS 协议中的国密密码套件。

在政务领域,国家电子政务外网和各级政府信息系统正在推进国密改造。政务云平台、电子公文系统、电子印章等应用场景均要求采用国密算法。特别是在电子政务证书体系中,SM2 证书已逐步取代 RSA 证书成为主流。

在技术实现层面,开源社区为国密算法的推广提供了重要支持。GmSSL 是国内最具影响力的国密算法开源库,提供了 SM2、SM3、SM4、SM9、ZUC 等算法的完整实现,支持 C/C++、Java、Python、Go 等多种语言绑定。Tongsuo(铜锁,原名 BabaSSL)是由蚂蚁集团主导开源的密码学库,基于 OpenSSL 分支开发,提供了对国密算法的原生支持,并已被多个大型互联网企业采用。此外,OpenSSL 自 1.1.1 版本开始也加入了 SM2、SM3、SM4 的支持,进一步降低了国密算法的使用门槛。

在 TLS 协议层面,国密 SSL(GMTLS)协议定义了基于国密算法的传输层安全规范,对应的密码套件包括 ECC_SM4_CBC_SM3、ECC_SM4_GCM_SM3 等。然而,国密 SSL 协议与国际标准 TLS 1.2/1.3 在握手流程和密码套件协商机制上存在差异,这给双模(同时支持国际标准和国密标准)部署带来了一定的技术挑战。目前,Nginx、Apache 和 Caddy 等主流 Web 服务器通过插件或补丁的方式支持国密 SSL。

硬件安全模块(HSM,Hardware Security Module)是国密算法在高安全场景中的重要载体。国内多家厂商生产符合 GM/T 0029—2014 标准的密码机和密码卡,提供 SM1(非公开算法,仅以芯片形式提供)、SM2、SM3、SM4、SM7(非公开算法)和 SM9 的硬件加速能力。这些 HSM 设备通常通过 SDF(密码设备应用接口)或 SKF(智能密码钥匙应用接口)标准接口与应用系统对接。

九、安全性总评与国际化

国密算法自公开以来,经历了国内外密码学界的广泛审查和分析。总体而言,各算法在其完整轮数下均未发现实际可行的攻击——SM4 完整 32 轮留有 8—9 轮安全余量,SM3 完整 64 步的碰撞与原像安全性均未受到实质威胁,SM2 曲线参数符合国际安全准则且无已知结构性弱点,SM9 所依赖的 BDH/DBDH 假设在推荐参数下仍然成立。

在国际标准化方面,国密算法的纳入 ISO 标准是一个重要的里程碑,但这并不意味着其在国际市场上获得了广泛采用。实际部署面临以下挑战:

第一,互操作性问题。国际主流的密码库、协议实现和硬件设备对国密算法的支持仍然有限。虽然 OpenSSL 已加入基本支持,但许多商业软件产品和云服务平台尚未原生集成国密算法。这意味着在跨国业务场景中,国密算法的使用可能遇到兼容性障碍。

第二,性能优化生态。AES 在全球范围内拥有成熟的硬件加速生态(如 Intel AES-NI、ARM AES 指令),而 SM4 的硬件加速主要集中在国产处理器上。不过,随着 ARM v8.4 架构引入了可选的 SM3 和 SM4 指令扩展,国际主流处理器对国密算法的硬件支持正在逐步改善。

第三,信任和透明度。国际密码学社区对国密算法的信任度与对 NIST 标准算法的信任度存在差距,这部分源于国密算法的设计准则和参数选择过程的公开透明程度不如 NIST 标准化流程。SM4 的 S 盒设计准则未完全公开、SM2 曲线参数的选取过程缺乏可验证的 “nothing-up-my-sleeve” 论证,都是需要持续推动改进的方面。

展望未来,国密算法体系面临着后量子密码学(Post-Quantum Cryptography)的挑战。当大规模量子计算机成为现实时,基于椭圆曲线离散对数问题的 SM2 和基于双线性对的 SM9 都将面临 Shor 算法的威胁,需要向后量子安全的替代方案迁移。对称密码 SM4 和杂凑算法 SM3 受量子计算的影响相对较小——Grover 算法仅将安全强度减半,通过增加密钥长度或输出长度即可应对。

国密算法体系的建设是中国在网络安全领域追求自主可控的重要实践。从技术角度看,SM2、SM3、SM4、SM9 和 ZUC 在设计质量上达到了国际同类算法的水准,经受住了广泛的密码分析检验。在全球密码学格局日益多元化的背景下,密码算法的多样性——正如生物多样性之于生态系统——降低了单一算法被攻破带来的系统性灾难风险。


密码学百科系列 · 第 47 篇

← 上一篇:门限密码学 | 系列目录 | 下一篇:区块链密码学

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .