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 对称密钥生成核心流程

下面这张图是我在项目中总结的密钥生成流程,每次做密钥管理方案设计时都会参考它。

对称密钥生成核心流程 1. 熵源采集 2. CSPRNG 生成随机字节 3. 算法适配(校验位/长度调整) 4. 质量验证(雪崩/统计) 5. 安全存储(HSM/密钥库) ⚠️ 常见错误 • 用密码短语直接当密钥 • 使用伪随机数(如 rand()) • 忽略奇偶校验位(DES) ✅ 推荐做法 • 使用 /dev/urandom • 或 CryptGenRandom 🔑 密钥长度速查 AES: 16/24/32 字节 3DES: 16 或 24 字节 SM4: 16 字节

2.6 避坑总结

做了这么多年密码学工程,对称密钥生成这块我踩过的坑不少。给你列几个最常见的:

  • 别用字符串哈希当密钥。 有人喜欢把密码做 MD5 后当 AES 密钥——这等于把密钥空间从 2^256 降到了口令的复杂度,毫无安全性可言。
  • 3DES 的弱密钥要排除。 DES 有 4 个弱密钥和 12 个半弱密钥,生成后要检查并重新生成。我记得有一次测试环境用了弱密钥,加解密居然对同一数据得到相同结果——吓出一身冷汗。
  • SM4 密钥不要和 AES 密钥混用。 虽然长度一样,但算法不同。我在一个项目中见过有人把 SM4 密钥直接喂给 AES 用——解密出来全是乱码,还以为是算法实现有 bug。
  • 密钥生成后立即做完整性校验。 生成后算个哈希存起来,下次使用时验证。这能防止存储介质静默损坏导致密钥出错。

最后提醒一句:密钥生成只是第一步。生成后的密钥如何分发、存储、轮换、销毁,才是密钥管理的重头戏。别在第一步就埋下隐患。