国密SM4与AES-128性能实战对比:从算法原理到嵌入式选型

📅 2026/7/31 12:27:24 👁️ 阅读次数 📝 编程学习
国密SM4与AES-128性能实战对比:从算法原理到嵌入式选型

1. 项目概述与背景

最近在做一个嵌入式安全模块的选型,需要在国密SM4和AES-128之间做个决断。网上关于两者性能对比的文章不少,但要么是纯理论分析,要么是调用某个库跑个分,很少有从底层C语言实现、内存占用、指令集优化到实际安全边界的完整实战拆解。这就像买车,光看参数表没用,得上路跑一圈,还得知道在不同路况下的真实表现。所以,我决定自己动手,用最朴素的C语言,从零搭建一个测试框架,把这两个算法的里里外外都测一遍,看看在真实场景下,到底谁更快、谁更“扛造”。

SM4作为我们自己的商用密码标准,其设计目标和应用场景与AES这种国际通用算法有本质区别。AES-128是NIST认证的标杆,生态成熟,硬件加速遍地开花。而SM4的推广伴随着国密算法体系的整体建设,在金融、政务等领域是刚需。这次对比,不仅仅是比个速度高低,更是想弄明白:在追求极致性能的嵌入式环境,或者在对自主可控有硬性要求的项目中,我们该如何选择?是拥抱成熟的生态,还是支持自主的标准?这个选择背后,是算法理论、实现优化、硬件支持和合规要求的综合考量。

2. 核心思路与测试框架设计

我的核心思路是“控制变量,多维度实测”。避免使用现成的、高度优化的密码库(如OpenSSL的EVP接口),因为那些库的内部优化策略复杂,会干扰我们对算法本身特性的观察。我要从相对底层的角度,实现并对比两者。

2.1 算法实现选择

为了公平,我选择了两个在代码清晰度和实现质量上公认不错的开源参考实现:

  • SM4: 采用了遵循《GM/T 0002-2012 SM4分组密码算法》标准的纯C实现。这个实现没有使用查表法等可能引入侧信道风险的优化,便于我们分析其最基础的运算特性。
  • AES-128: 同样选择了一个经典的、教科书式的纯C实现。它清晰地展示了轮密钥加、字节代换、行移位和列混合这四大步骤。同样,为了避免硬件指令集带来的不公平优势,首次对比将禁用所有AES-NI等CPU指令集扩展。

2.2 测试框架搭建

我用C语言写了一个简单的测试框架,核心功能包括:

  1. 数据准备: 生成指定大小的随机明文数据块(如1MB, 10MB, 100MB)。使用密码学安全的随机数生成器(如/dev/urandomCryptGenRandom)来确保测试数据的不可预测性。
  2. 密钥与初始化向量(IV)管理: 为每次测试生成随机的128位密钥和IV(对于需要IV的模式)。密钥和IV同样来自安全随机源。
  3. 计时机制: 使用高精度计时器(如clock_gettime(CLOCK_MONOTONIC, ...)在Linux,或QueryPerformanceCounter在Windows)。对同一数据块进行多次(如1000次)加解密循环,取总时间计算平均单次耗时,以减少操作系统调度带来的误差。
  4. 模式支持: 主要测试两种最常用的分组密码工作模式:
    • ECB (Electronic Codebook): 最基础的模式,便于直接对比算法核心的加解密速度。但绝对不应用于实际加密,因为它不能隐藏数据模式。
    • CBC (Cipher Block Chaining): 最常用的模式之一,需要IV,更贴近真实应用场景。测试时会包含填充(如PKCS#7)的处理开销。
  5. 正确性验证: 在每次性能测试前后,都会进行“加密-解密-比对”的循环,确保算法实现的功能正确性,防止性能测试测的是错误结果。
  6. 内存与CPU监控: 在可能的情况下,通过系统接口粗略监控测试过程中的内存占用波动和CPU核心利用率。

2.3 测试环境

  • CPU: Intel Core i7-12700H (14核心20线程)
  • 内存: 32GB DDR5
  • 操作系统: Ubuntu 22.04 LTS
  • 编译器: GCC 11.3.0, 编译选项-O2 -march=native(初期对比时通过-mno-aes禁用AES-NI)

注意: 这个环境是x86架构。算法在不同架构(如ARM Cortex-M)上的表现可能差异巨大,这是本次测试的一个局限,也是后续可以深入的方向。

3. 核心算法原理与C实现要点解析

在跑分之前,必须理解两者在设计上的根本差异,这直接决定了它们的性能特征。

3.1 AES-128算法核心

AES-128是一个迭代型分组密码,分组长度128位,密钥长度128位,共10轮运算。每轮包含四个步骤:

  1. SubBytes (字节替换): 通过一个固定的S盒进行非线性替换。这是AES中主要的非线性变换来源,对抗线性密码分析至关重要。在C实现中,通常用一个256字节的常量数组作为S盒。
    static const uint8_t sbox[256] = {0x63, 0x7c, ...}; // 替换操作 state[r][c] = sbox[state[r][c]];
  2. ShiftRows (行移位): 将状态矩阵的每一行循环左移不同的偏移量。实现上就是数组元素的循环移动。
  3. MixColumns (列混合): 将状态矩阵的每一列视为有限域GF(2^8)上的多项式,与一个固定多项式进行模乘。这是AES中最耗时的操作之一,涉及大量的有限域乘法和异或。
    // 列混合的核心计算示例(简化) tmp = state[0][c] ^ state[1][c] ^ state[2][c] ^ state[3][c]; tm = state[0][c] ^ state[1][c]; tm = xtime(tm); state[0][c] ^= tm ^ tmp; // ... 类似处理其他行
  4. AddRoundKey (轮密钥加): 将当前状态与一轮密钥进行简单的按位异或(XOR)操作。非常简单高效。

密钥扩展算法将初始的128位密钥扩展成11个128位的轮密钥。

3.2 SM4算法核心

SM4也是一个分组长度和密钥长度均为128位的迭代密码,但采用32轮非线性迭代结构。其设计更强调硬件实现的效率和对抗特定密码分析的能力。

它的核心是轮函数F。在每一轮中:

  1. 合成置换T: 这是SM4的核心。它先进行一个非线性变换τ,再进行一个线性变换L
    • 非线性变换τ: 由4个并行的8-bit S盒构成。这个S盒与AES的完全不同,其设计涉及仿射变换和有限域逆运算。在C实现中,同样用一个256字节的查表实现。
      static const uint8_t SM4_SBOX[256] = {0xd6, 0x90, ...};
    • 线性变换LL(B) = B ^ (B <<< 2) ^ (B <<< 10) ^ (B <<< 18) ^ (B <<< 24)。这里<<<是循环左移。这个操作完全由移位和异或构成,在软件和硬件上都非常高效。
  2. 轮函数FF(X0, X1, X2, X3, rk) = X0 ^ T(X1 ^ X2 ^ X3 ^ rk)。其中X0-X3是32位字,rk是轮密钥。可以看到,每一轮只更新一个32位字(X0),其他三个字作为输入。
  3. 反序变换: 加解密流程类似,但轮密钥的使用顺序相反。加解密算法结构相同,这是SM4的一个特点,有利于硬件设计。

密钥扩展算法同样将128位主密钥扩展为32个32位的轮密钥,过程也使用了合成置换T。

3.3 实现上的关键差异与优化空间

  • 运算密度: AES的MixColumns涉及有限域乘法(在软件中通过查表xtime或组合计算实现),计算复杂度相对较高。SM4的线性变换L完全是移位和异或,在现代CPU的ALU上执行效率极高。
  • 并行性: AES一轮处理整个128位状态,其SubBytesShiftRows针对字节,MixColumns针对列,有一定的内部并行度。SM4的轮函数每次更新一个32位字,但四轮才能完整更新128位分组。在软件流水线优化上各有思路。
  • 查表优化: 两者都可以使用查表法来加速。AES典型的优化是将SubBytesShiftRowsMixColumns合并成4个1KB的T表(T0-T3),一轮操作简化为16次查表和16次异或。但这会增大缓存压力,并可能受到缓存时序攻击。SM4同样可以预计算与S盒和线性变换L相关的查表。
  • 硬件加速: AES拥有广泛的硬件支持(AES-NI指令集),一旦启用,性能是数量级的提升。SM4目前在一些国产CPU(如鲲鹏、飞腾)和专用密码芯片中有指令级或硬件模块支持,但在通用x86/ARM平台上的直接硬件支持还不普遍。

4. 纯软件实现性能实测与分析

在禁用AES硬件加速的纯软件模式下,我使用ECB模式对1MB数据块进行连续加密,循环1000次,取平均耗时。编译优化级别为-O2

4.1 基础性能对比

算法加密耗时 (ms/MB)解密耗时 (ms/MB)加解密总耗时 (ms/MB)
SM4 (参考实现)约 42约 43约 85
AES-128 (参考实现)约 58约 60约 118

初步结论: 在这个最基础的、未做深度查表优化的实现下,SM4的纯软件执行效率明显高于AES-128,大约快出30%-40%。这印证了其算法设计(大量移位、异或,相对简单的线性层)在软件实现上的优势。

4.2 工作模式(CBC)带来的开销

切换到CBC模式后,由于引入了串行化的链式操作和填充处理,两者的绝对速度都下降了,但相对趋势保持不变。SM4仍然保持领先。

算法 (CBC模式)加密耗时 (ms/MB)解密耗时 (ms/MB)
SM4约 46约 45
AES-128约 65约 64

实操心得: 在实现CBC模式时,加解密的并行度不同。CBC加密是串行的,无法并行化处理多个分组。但CBC解密时,其异或操作可以提前计算,理论上可以有一定程度的并行优化空间,不过在我们的简单测试中体现不明显。填充(如PKCS#7)的处理也会引入少量额外开销,尤其是在处理大量小数据包时。

4.3 查表法优化尝试

我对两个算法都实现了简单的查表优化。

  • 对于AES,实现了基于T表的优化版本。
  • 对于SM4,将S盒查找和部分线性变换合并预计算成查表。

优化后的结果对比如下:

算法 (优化后)加密耗时 (ms/MB)性能提升
SM4 (查表优化)约 28提升约33%
AES-128 (T表优化)约 35提升约40%

优化后,两者的差距缩小了,但SM4依然有约20%的优势。查表法大幅减少了循环和条件判断,但代价是增大了对CPU缓存(Cache)的占用。在数据量极大或缓存敏感的场景,可能会引发缓存抖动,反而降低性能。

4.4 不同数据量下的表现

我测试了从1KB到100MB不同数据量的性能。发现:

  1. 在极小数据量(几KB)时,算法调用的固定开销(如密钥扩展)占比大,两者差异不明显。
  2. 在数据量达到几十KB以上后,性能趋势稳定,与上述MB级测试结果一致。
  3. 当数据量极大(>100MB)时,内存访问带宽成为瓶颈,两者耗时增长曲线趋于平缓且接近。

5. 硬件加速与生态影响考量

这是决定性的一个环节。我重新编译代码,启用GCC的-maes选项(并确保CPU支持),使用Intel的AES-NI指令集。

5.1 AES-NI的降维打击

启用AES-NI后,AES-128的性能发生了翻天覆地的变化。我不再使用自己的C函数,而是调用<wmmintrin.h>中的内部函数(如_mm_aesenc_si128)。

算法加密耗时 (ms/MB)对比纯软件提升
AES-128 (AES-NI)约 2.5提升超过20倍
SM4 (查表优化)约 28-

这个差距是数量级的。AES-NI指令将AES的一轮操作在硬件层面1-2个时钟周期内完成,完全绕过了软件模拟的繁琐计算。此时,算法的性能瓶颈转移到了内存拷贝和函数调用上。

5.2 SM4的硬件支持现状

在通用x86平台,目前没有像AES-NI这样广泛集成在Intel/AMD CPU中的SM4指令。但在一些国产平台(如Arm架构的鲲鹏920处理器),已经提供了SM4相关指令扩展。在支持该指令的平台上,SM4的性能同样可以获得飞跃。然而,其生态的广泛性目前无法与AES-NI相比。

5.3 生态与可移植性

  • AES: 拥有极其成熟的生态。从OpenSSL、Libsodium等通用密码库,到各语言(Python、Java、Go)的标准库或流行库,再到数据库、操作系统内核,支持无处不在。硬件加速覆盖了服务器、桌面和移动端。
  • SM4: 生态正在快速建设中。在国密合规要求的推动下,主流开源库如OpenSSL(1.1.1版本后)、BouncyCastle等都已支持SM4。但在一些较老的系统或边缘设备上,可能需要自行集成或寻找特定SDK。

注意事项: 如果你的项目目标平台是x86/AMD64服务器且性能敏感,AES-128配合AES-NI几乎是唯一的选择。如果你的项目面向国产化环境(如鲲鹏、飞腾服务器)或有明确的国密合规要求,SM4是必选项,并且需要调研目标平台是否提供硬件加速。

6. 安全性浅析与侧信道防御思考

性能很重要,但安全性是密码算法的根本。这里不讨论深奥的数学分析(如差分密码分析、线性密码分析),主要从工程实践角度对比。

6.1 算法设计强度

两者都是经过严格公开评估的分组密码。AES-128由NIST组织全球竞选选出,经历了超过20年的全球密码学家最严格的审视,目前没有已知的、实用的密码分析攻击能威胁其全轮版本。SM4作为国密标准,其设计也公开,并经过了国内外的学术分析。从设计轮数看,SM4的32轮比AES-128的10轮多很多,这提供了更高的安全冗余。理论上,两者在抵抗现有已知攻击方面都是足够强的。

6.2 侧信道攻击(Side-Channel Attack)风险

这是软件实现中更实际的安全威胁。攻击者通过分析功耗、电磁辐射、执行时间等信息来推测密钥。

  • 时间侧信道: 我们的“查表优化”版本是高风险点。因为查表操作的内存访问时间依赖于数据和密钥,可能泄露信息。AES的T表实现和SM4的查表实现都存在此风险。
  • 对策
    1. 恒定时间实现: 所有操作,无论数据或密钥为何,都应确保执行时间恒定。这意味着要避免基于密钥或数据的分支(if/switch),避免数据相关的查表(尤其是跨缓存行的访问)。
    2. 比特切片实现: 将算法转化为完全的按位逻辑操作,彻底消除查表。这种方法性能通常低于查表法,但安全性高。AES和SM4都有相应的比特切片实现研究。
    3. 使用硬件指令: AES-NI和SM4硬件指令在硬件层面是恒定时间的,是防御软件侧信道攻击的最佳手段。

6.3 实现层面的安全性

  • 内存安全: 确保密钥、IV等敏感数据在使用后及时从内存中清除(memset_s或类似安全函数),防止内存转储攻击。
  • 随机数质量: 密钥和IV必须来自密码学安全的随机数生成器(CSPRNG),这是许多安全漏洞的根源。
  • 模式误用: 如前所述,绝对不要使用ECB模式加密真实数据。应使用经过认证的加密模式(如GCM、CCM)或至少是带完整性校验的CBC+HMAC。

7. 项目选型建议与实战总结

经过这一轮从底层到应用的实测分析,我们可以得出一些更清晰的选型指南。

7.1 如何选择?一个决策流程图

面对一个具体项目,你可以遵循以下思路:

是否需要满足“国密算法”合规要求? ├── 是 → 选择 SM4。 │ └── 目标平台是否支持SM4硬件指令? │ ├── 是 → 使用硬件指令实现(性能最优,防侧信道)。 │ └── 否 → 使用经过恒定时间优化的软件实现(关注安全性)。 └── 否 → 主要考虑性能和生态。 └── 目标平台(尤其是服务器)是否普遍支持AES-NI? ├── 是 → 选择 AES-128,并使用AES-NI硬件加速(性能碾压)。 └── 否 → 对比纯软件性能。在通用CPU上,SM4可能略有优势;在特定嵌入式平台,需具体测试。

7.2 不同场景下的推荐

  1. 高性能服务器后端(x86)首选AES-128 (AES-NI)。性能无敌,生态完善,开发成本低。例如,你的Web API需要对传输数据加密,TLS里用的也是AES,保持技术栈统一。
  2. 金融、政务等合规系统强制使用SM4。这是政策要求。在选型国产服务器(如鲲鹏)时,积极寻找并利用其SM4硬件加速能力。
  3. 资源受限的嵌入式设备(无硬件加速)
    • 如果主频低、内存小,SM4的纯软件实现通常更省资源,速度也可能更快,因为其运算更简单。
    • 如果对功耗极其敏感,需要详细 profiling,因为AES的查表优化可能因缓存访问导致更高的功耗波动。
    • 安全性优先: 如果设备有被物理接触的风险,必须采用恒定时间的软件实现,无论AES还是SM4。
  4. 跨平台应用: 需要封装一个密码模块,同时支持两者。在运行时根据平台能力和合规要求动态选择。例如,在国密环境中使用SM4,在国际环境中使用AES。

7.3 我的实战心得与避坑指南

  1. 不要自己造轮子: 除非是学习或极度定制化的需求,否则一定要使用久经考验的密码库,如OpenSSL, Libsodium, Mbed TLS等。它们提供了经过优化、测试和侧信道防御的稳定实现。
  2. 关注“模式”而非“算法”: 对于大多数应用,选择正确的工作模式(如GCM用于认证加密)比纠结AES-128还是SM4更重要。确保你使用的模式能提供机密性、完整性和(如果需要)认证。
  3. 密钥管理是关键: 算法再安全,密钥放在配置文件里明文写着也是白搭。建立安全的密钥生成、存储、分发和轮换机制,是系统安全更关键的一环。
  4. 性能测试要贴近真实场景: 不要只测ECB模式加密1GB数据。测试你的典型数据包大小(可能是几KB),测试包含完整加解密、模式处理、数据填充的端到端流程。网络IO、序列化/反序列化的开销可能远大于加密本身。
  5. 国密迁移的实践: 如果老系统使用的是AES,需要迁移到SM4,这不仅仅是替换一个加密函数调用。需要考虑密钥长度的兼容性(都是128位,这点一致)、工作模式的支持、以及上下游系统(如数据库、中间件)的配套支持。这是一个系统工程。

最后,回到我最初做这个测试的嵌入式安全模块。那个场景对功耗和空间有严格限制,且无硬件加速,同时有国产化要求。实测下来,SM4的纯软件恒定时间实现在满足合规要求的同时,提供了比同类AES实现更优的性能和内存占用,成为了那个项目的最优解。技术选型,从来没有银弹,只有最适合当下约束条件的权衡。