3. SM4密钥生成漏洞:弱密钥、固定IV、随机数种子复用
SM4作为国密标准中的对称分组密码算法,本身设计是安全的。但我在实际项目里见过太多「算法没问题,实现全是洞」的案例了。密钥生成这一环,恰恰是攻击者最喜欢下手的地方。
说白了,SM4的密钥长度是128位,暴力破解不现实。但如果你用了弱密钥、固定IV、或者随机数种子复用,那攻击者根本不需要暴力破解——你等于把门敞开了。
3.1 弱密钥问题:你以为随机了,其实没有
SM4的密钥调度算法会把128位主密钥扩展成32个轮密钥。如果主密钥本身熵不够,或者密钥扩展后出现规律性,那整个加密就形同虚设。
常见的弱密钥场景:
- 全零密钥:0x00000000000000000000000000000000。我在一次金融终端对接时,发现对方测试环境居然用了全零密钥上线——还好是内网,不然数据等于明文。
- 全F密钥:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF。某些硬件实现里,全F密钥会导致轮密钥出现对称性。
- ASCII字符串直接填充:比如把"1234567890abcdef"直接当密钥用。这种密钥的字节分布极不均匀,熵值不到64位。
- 时间戳截断:用毫秒时间戳的低32位重复4次拼成128位。攻击者只要知道大致时间窗口,就能穷举。
⚠️ 避坑指南
我曾经在一个物联网项目中,发现设备端用芯片唯一ID的低64位重复两次作为SM4密钥。看似随机,但同批次芯片的ID只有后32位不同——相当于所有设备的密钥空间只有2^32。攻击者拿到一台设备,就能推导出同批次其他设备的密钥。
我曾经在一个物联网项目中,发现设备端用芯片唯一ID的低64位重复两次作为SM4密钥。看似随机,但同批次芯片的ID只有后32位不同——相当于所有设备的密钥空间只有2^32。攻击者拿到一台设备,就能推导出同批次其他设备的密钥。
3.2 固定IV:CBC模式下的灾难
SM4常用的工作模式是CBC。CBC模式的安全性高度依赖IV的不可预测性。如果IV固定,会带来两个致命问题:
- 相同明文产生相同密文:攻击者可以识别出重复的加密数据块。比如加密"登录成功"和"登录失败",固定IV下密文模式固定,攻击者一眼就能看出状态。
- IV预测攻击:如果IV是固定的,攻击者可以构造特殊明文来逐步还原密钥。这在SSL/TLS的BEAST攻击里已经演示得很清楚了。
我见过最离谱的一次:某政务系统用SM4-CBC加密数据库中的身份证号,IV写死在配置文件里,而且所有表共用同一个IV。这意味着相同身份证号的密文完全一样——加密了个寂寞。
✅ 正确做法
IV必须每次加密随机生成,且长度固定为16字节(128位)。IV不需要保密,可以随密文一起传输。推荐做法:
IV必须每次加密随机生成,且长度固定为16字节(128位)。IV不需要保密,可以随密文一起传输。推荐做法:
iv = os.urandom(16) 然后拼在密文前面,解密时先取前16字节作为IV。
3.3 随机数种子复用:最隐蔽的漏洞
这个坑比前两个更隐蔽。很多开发者知道要用随机IV、随机密钥,但随机数的生成方式错了。
典型错误代码:
// 错误示例:使用固定种子
srand(10086); // 固定种子
uint8_t key[16];
for (int i = 0; i < 16; i++) {
key[i] = rand() % 256;
}
// 错误示例:使用时间种子且快速调用
srand(time(NULL));
uint8_t iv[16];
for (int i = 0; i < 16; i++) {
iv[i] = rand() % 256;
}
// 如果两次调用间隔小于1秒,time(NULL)返回相同值,IV完全一样
为什么会这样?因为rand()是伪随机数生成器,种子相同则序列完全相同。攻击者只要知道或猜到种子,就能重现整个密钥和IV序列。
种子复用的攻击场景:
| 场景 | 种子来源 | 攻击难度 | 实际案例 |
|---|---|---|---|
| 设备批量生产 | 芯片ID低32位 | 低 | 同批次设备密钥可预测 |
| 容器启动 | 容器启动时间戳 | 中 | 同一秒启动的容器密钥相同 |
| 虚拟机快照恢复 | 快照时的熵池状态 | 高 | 恢复后随机数序列完全重复 |
| 嵌入式设备上电 | 上电时间+计数器 | 低 | 冷启动后密钥可预测 |
💡 我的建议
在Linux/Unix环境下,永远使用
在Linux/Unix环境下,永远使用
/dev/urandom 或 getrandom() 系统调用。在嵌入式环境中,如果硬件没有TRNG(真随机数发生器),至少要用多源熵混合:ADC噪声 + 时钟抖动 + 外部中断时间戳。不要自己实现随机数生成器——这是密码学里最容易翻车的地方之一。
3.4 知识体系:SM4密钥安全全景
下面这张图把SM4密钥生成过程中的风险点和防护措施串起来了。我建议你把它存下来,做代码审查时对照着看。
3.5 实战检查清单
每次做SM4密钥相关代码审查时,我习惯按下面这个清单过一遍。你也试试看:
- 密钥来源检查:密钥是硬编码的吗?是从配置文件读取的吗?如果是,配置文件权限设置了吗?
- 密钥熵检查:密钥的字节分布是否均匀?有没有出现重复字节模式?
- IV生成检查:每次加密是否都生成了新的IV?IV是否使用了安全随机数生成器?
- 随机数种子检查:代码中是否有
srand()、srandom()调用?种子来源是否可预测? - 密钥生命周期检查:密钥使用完后是否安全擦除?内存中是否有残留?
🔑 一句话总结
SM4算法本身没问题,问题出在密钥怎么来、IV怎么生、随机数怎么取。这三个点守住,SM4的安全性就有保障。守不住,算法再强也没用。
SM4算法本身没问题,问题出在密钥怎么来、IV怎么生、随机数怎么取。这三个点守住,SM4的安全性就有保障。守不住,算法再强也没用。