第1章 · SM4算法概述与国家标准归纳总结

做密码学这么多年,每次接手国密项目,第一件事就是把SM4的参数配置拿出来重新对一遍。为啥?因为这东西看起来简单,但坑多。SM4是国家密码管理局2006年发布的对称分组密码标准,全称《GM/T 0002-2012 SM4分组密码算法》。说白了你只需要记住三点:它是国标、它是分组密码、它是用来加密数据的。

1.1 什么是SM4算法

SM4算法,以前叫SMS4,是咱们国家自主设计的对称加密算法。我在2016年第一次接触它的时候,心里还犯嘀咕:跟AES比到底差多少?后来调了几个项目才发现——SM4在软件实现上其实不慢,硬件实现更稳。关键它是国家标准,国内很多合规场景必须用它。

简单讲,SM4的分组长度是128比特,密钥长度也是128比特。它属于Feistel结构,迭代32轮。没接触过Feistel?没关系,你就把它想象成一个分左右两半的绞肉机,经过32次来回挤压,明文就变成密文了。解密过程几乎一样,只是轮密钥的顺序反过来。

核心参数一览

  • 分组长度:128比特(即16字节)
  • 密钥长度:128比特(即16字节)
  • 算法结构:Feistel网络,32轮迭代
  • 模式支持:ECB、CBC、CFB、OFB、CTR等(标准中推荐CBC和CTR)
  • S盒:固定8×8 S盒,共256个元素

1.2 国家标准体系与演进

SM4最早出现在《GM/T 0002-2012》里。2016年之后,国家密码管理局把它升级到了GB/T 32907-2016,变成了国标。等于说原来只是行业标准,后来成了整个国家的推荐标准。嗯,这里要注意:很多商用密码产品要求必须支持GB/T 32907版本。

我在一次等保测评项目中遇到过这种情况——客户用了某厂商的SM4加密卡,结果评测时发现它只实现了GM/T 0002-2012的老版本,有些API参数不兼容新国标。虽然算法本质一样,但接口规范有差异,最后折腾了两周才搞定。所以我在课程里一直强调:版本要看清。

SM4相关国家标准归纳
标准编号发布年份主要内容状态
GM/T 0002-20122012SM4分组密码算法定义行业标准(部分被替代)
GB/T 32907-20162016SM4分组密码算法(国标版)现行推荐标准
GM/T 0042-20162016SM4算法的CBC/CTR模式规范现行
GB/T 38624-20202020SM4在物联网场景下的应用指南现行

你想想看,一个算法如果只是密码算法本身,你写代码时只需要把加密函数调通就行了。但实际落地上不同标准的差异主要体现在模式选择、填充方式、初始向量(IV)长度这些细节上。比如GB/T 32907-2016明确要求IV必须是128比特的随机数,而早期的某些行业文档对IV没有强制规定。

1.3 算法基本流程速览

我习惯把SM4的加密过程分成三步走:密钥扩展 → 轮函数迭代 → 反序变换。别看它32轮,其实每轮做的事情都一样——左半部分和本轮子密钥喂进F函数,输出跟右半部分异或,然后左右交换。是不是很简单?

  1. 密钥扩展:给定16字节密钥,通过固定系统参数FK和CK生成32个32位的轮密钥rk[0] ~ rk[31]。
  2. 32轮迭代加密:将128位明文分成4个32位字(X0, X1, X2, X3),每轮用F函数更新X3。
  3. 反序变换:最后对输出做反序处理,得到密文。

注意:SM4的S盒是固定的,这一点跟AES完全不同。你不需要自己生成S盒,直接用标准给出的256字节表。我见过有人好奇写了个S盒生成器,结果加密解密的兼容性全崩了——千万别自行“优化”S盒。

1.4 加密模式与参数配置

说实话,很多人以为SM4就是调一下库,传个密钥和明文就完事了。真正踩坑的是模式选择和IV配置。我个人项目里最喜欢用CBC模式,因为它能掩盖明文的重复模式。但CTR模式在并行处理场景下更有优势。

我的建议:如果做文件加密,用CBC模式加上PKCS7填充;如果做网络数据流加密,用CTR模式不需要填充,而且能支持随机读取。

下面我用一段简单的伪代码展示SM4在CBC模式下的参数配置流程。注意IV必须每次随机生成,千万别写死。

function sm4_cbc_encrypt(key, iv, plaintext) {
    // 参数验证
    if (key.length !== 16 || iv.length !== 16) {
        throw new Error("密钥和IV长度必须为16字节");
    }
    // 密钥扩展
    rk = sm4_key_expansion(key);
    // 分组处理,PKCS7填充
    blocks = pad_pkcs7(plaintext, 16);
    ciphertext = [];
    prev_block = iv;
    for (block in blocks) {
        xored = xor(block, prev_block);  // CBC异或前一个密文块
        enc = sm4_encrypt_block(xored, rk);
        ciphertext.append(enc);
        prev_block = enc;
    }
    return ciphertext;
}

代码里最关键的就是“prev_block”的初始值必须用IV。IV如果重复,相同明文块生成的密文块也相同,那CBC模式就失去意义了。以前我在测试环境里偷懒用了全零IV,结果被安全审计发现了,差点被扣绩效……

1.5 知识体系结构

这一章把SM4的“块”拼得差不多该歇口气了。下面这张SVG图帮你梳理本章的核心逻辑,方便后续查看。

SM4算法知识体系(第1章) 分组长度128比特 密钥长度128比特 32轮Feistel结构 密钥扩展(FK/CK参数) 轮函数F + S盒 反序变换 CBC模式 CTR模式 ECB/OFB等模式 关键理解 1. 国家标准有新旧版本,商用落地认准GB/T 32907-2016 2. 密钥扩展、S盒都是固定硬编码,不可自定义 3. 模式选择直接影响IV和填充策略,根据场景选CBC或CTR