4. 密钥生成与存储

密钥生成是权限系统的起点。你想想看,后面所有的签名、加密、认证,都依赖一把足够强的密钥。如果生成这一步就出了漏洞,后面做再多防护也是白搭。这一章我们聊透两件事:密钥怎么生、生完后放在哪。

4.1 密钥生成算法

密钥生成的核心是“不可预测性”。说白了,就是随机数要够乱。我见过一些团队图省事,直接用 rand() 或者系统时间戳做种子来生成密钥——嗯,这种密钥基本等于明文。

4.1.1 对称密钥生成

以 AES-256 为例,密钥就是 256 比特的随机数。生成过程很简单:

  1. 从系统真随机数源(如 /dev/urandom、Intel RDRAND)获取 32 字节。
  2. 校验熵池是否充足(Linux 下可读 /proc/sys/kernel/random/entropy_avail)。
  3. 将原始字节直接作为密钥,或以 HKDF 做一次熵提取。

我在项目中遇到过一个问题:某云环境下的虚拟机启动时熵池几乎为空,RDRAND 指令又被 KVM 拦截,结果生成了一堆全零密钥。幸亏测试环境先暴露了,否则上线就完了。

我曾经接手过一个历史项目,RSA 密钥生成只用了一种伪随机算法。业务跑了两年才发现密钥可被预测,攻击者能直接算出私钥。从那以后,我要求所有密钥生成代码必须显式依赖硬件随机数或操作系统 CSPRNG。

4.1.2 非对称密钥生成

RSA 需要生成两个大素数,ECC 需要挑选椭圆曲线上的基点。算法不同,但共同点是计算量大,且对随机性要求极高。

算法 推荐位数/曲线 随机数消耗 生成耗时(参考)
AES-256 256 bit 32 字节 < 1ms
RSA-2048 2048 bit 数百字节(素性测试) 200ms~5s
ECC P-256 secp256r1 32 字节 + 标量乘法 10~50ms

我个人习惯在非对称密钥生成后立即做一次自检:用新密钥加密/签名一段已知数据,验证结果正确性。这一步能提前发现数学异常,避免把坏密钥写入存储。

4.2 安全存储方案

密钥生成好了,接下来要存。常见的方案有三种:软件存储、硬件安全模块(HSM)、密钥管理服务(KMS)。我按风险从高到低排个序。

4.2.1 软件存储(不推荐)

直接把密钥写在配置文件、数据库字段或环境变量里。你想想看,只要服务器被人登录,密钥就等于白送。即便是加密存储,解密钥匙也在内存里,攻击者 dump 一下进程就拿到了。

我个人建议:除非是开发环境临时调试,否则永远别用软件存密钥。非要存的话,至少用操作系统密钥环(如 Linux Key Retention Service),且只允许 root 访问。

4.2.2 硬件安全模块(HSM)

HSM 是一种专用硬件,密钥永远不离开设备。应用通过 PKCS#11 或 JCE 接口调用签名/加密操作,输入的只是原始数据和操作指令,输出的就是结果。密钥本身以加密形式存储在 HSM 内部 Flash 中,即使物理打开芯片也无法直接读出。

我在项目中部署过 Thales Luna HSM,最深的体会是:初始化和备份策略极其重要。一旦主密钥(Master Key)丢失,所有子密钥全部报废。我曾经因为备份文件被误删,导致整个 HSM 中的 CA 证书链作废,花了三天才从异地灾备恢复。

// 伪代码:通过 PKCS#11 生成 AES 密钥并存储在 HSM 中
CK_OBJECT_HANDLE hKey;
CK_MECHANISM mech = {CKM_AES_KEY_GEN, NULL_PTR, 0};
CK_ULONG keyLen = 32; // 256 bit

CK_ATTRIBUTE template[] = {
    {CKA_CLASS, &keyClass, sizeof(keyClass)},
    {CKA_KEY_TYPE, &keyType, sizeof(keyType)},
    {CKA_ENCRYPT, &trueVal, sizeof(trueVal)},
    {CKA_DECRYPT, &trueVal, sizeof(trueVal)},
    {CKA_VALUE_LEN, &keyLen, sizeof(keyLen)}
};

CK_SESSION_HANDLE hSession;
C_GenerateKey(hSession, &mech, template, 3, &hKey);
// hKey 可以用于后续操作,但密钥值永远不会暴露给应用

4.2.3 密钥管理服务(KMS)

云原生的选择。AWS KMS、Azure Key Vault、阿里云 KMS 本质上都是把 HSM 的管理能力封装成 API。你调用 CreateKey 接口,平台在底层 HSM 中生成密钥,返回给你一个 Key ID。后续调用 Sign、Encrypt 都通过 Key ID 路由到对应密钥。

关键区别:HSM 你自管硬件,KMS 你管 API 权限。对于 RBAC 系统,KMS 天然配合 IAM 策略,可以精确控制哪个角色能 UseKey、哪个角色能 CreateKey。我个人倾向在云环境中优先用 KMS,运维成本低很多。

4.2.4 方案对比

维度 软件存储 HSM KMS
密钥可导出性 完全可导出 不可导出(除非备份) 不可导出
性能 快(CPU 计算) 中(硬件加速) 慢(网络延迟 + 限流)
合规认证 无 FIPS 140-2 Level 3+ FIPS 140-2 Level 2/3
运维成本 低 高(硬件+专线+备份) 中(按调用量付费)
RBAC 集成 需要自建 通过 ACL/分区 原生 IAM 集成

4.3 知识体系图

下面这张图把密钥生成到存储的完整链路串起来了。节点颜色按类别区分:随机数源(蓝色)、生成引擎(紫色)、存储方案(绿色/橙色/红色)。

密钥生成与存储核心流程 随机数源 /dev/urandom / RDRAND 熵池校验 entropy_avail > 256 对称密钥生成 AES-256 / ChaCha20 非对称密钥生成 RSA-2048 / ECC P-256 密钥自检 签名/加密验证 对称 密钥 非对称 密钥 软件存储 不推荐 / 仅开发环境 HSM 硬件隔离 / PKCS#11 FIPS 140-2 Level 3 KMS 云原生 / IAM 集成 按调用付费 / 自动轮转 ⚠ 避坑点 随机数不足→密钥弱化 HSM 主密钥必须异地备份

4.4 选型建议

没有银弹。我通常根据这几个条件决策:

  • 合规要求:金融、政务场景,选 HSM(Level 3 以上),物理隔离是硬门槛。
  • 团队运维能力:小团队上 KMS,别自己搭 HSM,我曾经见过运维把 HSM 网络暴露到了公网。
  • 性能敏感:把对称密钥缓存在本地(使用安全飞地或封闭内存),只在初始化时从 KMS 获取。

我个人习惯:无论选哪种方案,都做一层抽象。对外暴露 KeyVault 接口,实现可以切换。这样后期从软件存储迁移到 HSM 时,业务代码基本不用改。我上一家公司就是用这种架构,从自建 Vault 迁移到 AWS KMS,只改了一行配置。

嗯,密钥生成与存储就讲这么多。记住核心:随机源要硬、存储要封、访问要鉴。下节我们聊密钥的生命周期轮转策略——这部分和 RBAC 权限的联动更紧密。但那是另一章的事了。