2. 对称密钥生成:AES、DES/3DES、SM4 实战
对称密钥生成,说白了就是给加密算法找一把合适的「钥匙」。钥匙不对,门打不开;钥匙太弱,门一踹就开。我在项目中见过太多次因为密钥生成不规范导致的安全事故,所以这一章咱们把 AES、DES/3DES 和国密 SM4 的密钥生成讲透。
2.1 AES 密钥生成:128/192/256 位的选择
AES 是目前最主流的对称加密算法。它的密钥长度有三种:128 位、192 位、256 位。我个人习惯,能上 256 就上 256,除非有性能瓶颈。
核心原则:AES 密钥必须是均匀随机的字节序列。不要用密码短语直接当密钥,一定要经过 KDF(密钥派生函数)处理。
2.1.1 密钥长度与轮数对照
| 密钥长度 | 轮数 | 安全等级 | 典型场景 |
|---|---|---|---|
| 128 位 | 10 轮 | 足够安全 | 一般商业加密 |
| 192 位 | 12 轮 | 较高 | 金融、政务 |
| 256 位 | 14 轮 | 最高 | 顶级机密、长期存储 |
你想想看,128 位 AES 的暴力破解需要 2^128 次尝试,以目前算力来说,这已经是个天文数字了。但为什么还要用 256 位?因为量子计算来了之后,Grover 算法能把安全强度砍半——128 位变成 64 位,那就危险了。256 位变成 128 位,依然安全。
2.1.2 生成方式
我建议用操作系统提供的 CSPRNG(密码学安全伪随机数生成器)。别自己写随机数,那是大忌。
// C# 示例:生成 256 位 AES 密钥
using System.Security.Cryptography;
byte[] aesKey = new byte[32]; // 32 字节 = 256 位
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(aesKey);
}
// aesKey 现在就是一个合格的 AES-256 密钥
小技巧:生成后可以做个「雪崩测试」——改一个比特,看密文是否完全变化。我在项目中用这个办法抓出过几次硬件随机数生成器的 bug。
2.2 DES/3DES 密钥生成:老算法,新坑
DES 现在基本被弃用了,但 3DES 在一些遗留系统里还在跑。嗯,这里要注意,3DES 的密钥生成有特殊规则。
2.2.1 DES 密钥:奇偶校验位
DES 密钥是 64 位,但有效位只有 56 位。为什么?因为每 8 位中有一位是奇偶校验位。我曾经在迁移一个老系统时,发现对方直接把 8 字节随机数当 DES 密钥用,结果校验位不对,加解密偶尔失败——排查了两天才找到原因。
// 正确的 DES 密钥生成:设置奇偶校验位
byte[] desKey = new byte[8]; // 64 位
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(desKey);
// 调整每个字节的最低有效位为奇校验
for (int i = 0; i < desKey.Length; i++)
{
if (BitCount(desKey[i]) % 2 == 0)
desKey[i] ^= 0x01; // 翻转最低位
}
}
2.2.2 3DES 密钥:两密钥还是三密钥?
3DES 本质上是把 DES 跑三遍。密钥有两种模式:
- 两密钥 3DES(TDEA 密钥选项 2): K1 = K3,有效长度 112 位
- 三密钥 3DES(TDEA 密钥选项 1): K1 ≠ K2 ≠ K3,有效长度 168 位
避坑指南:我曾经遇到一个项目,用三密钥模式但 K1 和 K3 设成了相同值——等于降级成了两密钥模式,安全强度直接打折。生成时一定要检查三个子密钥是否互不相同。
// 三密钥 3DES 生成示例
byte[] key1 = GenerateDesKey(); // 8 字节,含校验位
byte[] key2 = GenerateDesKey();
byte[] key3 = GenerateDesKey();
// 检查互异性
if (key1.SequenceEqual(key2) || key2.SequenceEqual(key3) || key1.SequenceEqual(key3))
{
throw new Exception("3DES 子密钥不能相同!");
}
byte[] tripleDesKey = key1.Concat(key2).Concat(key3).ToArray(); // 24 字节
2.3 SM4 国密算法密钥生成:国产标准
SM4 是国密标准中的对称加密算法,密钥固定 128 位。它的密钥生成和 AES 类似,但有个关键区别——SM4 的密钥扩展算法和 S 盒是公开的,但生成密钥时同样需要高质量随机数。
2.3.1 SM4 密钥参数
| 参数 | 值 | 说明 |
|---|---|---|
| 密钥长度 | 128 位(16 字节) | 固定,不可变 |
| 分组长度 | 128 位 | 与 AES 相同 |
| 轮数 | 32 轮 | 比 AES 多,但硬件实现效率高 |
| 密钥扩展 | 固定算法 | 生成 32 个轮密钥 |
2.3.2 生成方式
SM4 密钥生成和 AES-128 完全一样——取 16 字节高质量随机数即可。但要注意,国密标准要求使用国家密码管理局批准的随机数发生器。
// SM4 密钥生成(Java 示例)
import java.security.SecureRandom;
byte[] sm4Key = new byte[16]; // 128 位
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(sm4Key);
// sm4Key 可直接用于 SM4 加解密
个人经验:在对接国密改造项目时,我发现很多厂商直接用 AES 的随机数生成器来生成 SM4 密钥——这其实没问题,因为随机数本身不依赖算法。但密钥存储和传输必须走国密标准(如使用 SM2 加密传输 SM4 密钥)。
2.4 三种算法密钥生成对比
| 特性 | AES | 3DES | SM4 |
|---|---|---|---|
| 密钥长度 | 128/192/256 位 | 112 或 168 位(有效) | 128 位(固定) |
| 随机数要求 | 均匀随机 | 均匀随机 + 奇偶校验 | 均匀随机 |
| 特殊处理 | 无 | 需设置校验位 | 无 |
| 推荐生成方式 | CSPRNG 直接生成 | CSPRNG + 校验位调整 | CSPRNG 直接生成 |
| 当前状态 | 强烈推荐 | 建议迁移 | 国密合规 |
2.5 对称密钥生成核心流程
下面这张图是我在项目中总结的密钥生成流程,每次做密钥管理方案设计时都会参考它。
2.6 避坑总结
做了这么多年密码学工程,对称密钥生成这块我踩过的坑不少。给你列几个最常见的:
- 别用字符串哈希当密钥。 有人喜欢把密码做 MD5 后当 AES 密钥——这等于把密钥空间从 2^256 降到了口令的复杂度,毫无安全性可言。
- 3DES 的弱密钥要排除。 DES 有 4 个弱密钥和 12 个半弱密钥,生成后要检查并重新生成。我记得有一次测试环境用了弱密钥,加解密居然对同一数据得到相同结果——吓出一身冷汗。
- SM4 密钥不要和 AES 密钥混用。 虽然长度一样,但算法不同。我在一个项目中见过有人把 SM4 密钥直接喂给 AES 用——解密出来全是乱码,还以为是算法实现有 bug。
- 密钥生成后立即做完整性校验。 生成后算个哈希存起来,下次使用时验证。这能防止存储介质静默损坏导致密钥出错。
最后提醒一句:密钥生成只是第一步。生成后的密钥如何分发、存储、轮换、销毁,才是密钥管理的重头戏。别在第一步就埋下隐患。