第4章 密钥管理:生成、存储、轮换、泄露风险
密钥是整个加密体系的命根子。说句实在的,我见过太多接口被攻破的案例,根子都出在密钥管理上。你算法选得再强,密钥如果裸奔,那跟没锁门差不多。
咱们这一章就专门聊密钥的完整生命周期——怎么安全地生成、怎么存、什么时候换、万一泄露怎么兜底。这些东西,我在实际渗透测试中反复验证过,每一个坑都是真金白银换来的。
4.1 密钥生成:安全与随机性
密钥生成的第一个要求——必须是真随机或者密码学安全的伪随机。很多人图省事,用 Math.random() 去生成密钥,这其实是大忌。我曾在客户代码里看到直接用 new Random().nextBytes() 拼 AES 密钥,那个随机数生成器如果种子可预测,密钥就等于明文。
正确的做法:用系统提供的安全随机数生成器。
// Java 安全随机数示例
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, SecureRandom.getInstanceStrong());
SecretKey key = keyGen.generateKey();
// OpenSSL 命令行生成 256 位随机密钥
openssl rand -hex 32 > aes.key
SecureRandom 而非普通 Random,以及种子是否被固定。
4.2 密钥存储:保护静态密钥
密钥生成之后放哪?放配置文件里?放数据库字段里?你想想看,如果攻击者拿到服务器权限,第一件事就是翻配置文件找密钥。
| 存储方式 | 安全等级 | 常见风险 |
|---|---|---|
| 明文硬编码在代码中 | 极低 | 源码泄露即密钥泄露 |
| 配置文件(属性文件) | 低 | 服务器被入侵后直接读取 |
| 环境变量 | 中 | 进程信息泄露、/proc 文件系统暴露 |
| 密钥管理服务(KMS) | 高 | 依赖云服务自身安全 |
| 硬件安全模块(HSM) | 极高 | 成本高,密钥物理保护 |
我在项目中遇到过一个案例:某互联网金融公司把 RSA 私钥放在 Git 仓库里,注释写着“临时密钥,上线后换”。结果那个“临时”用了两年,直到被安全扫描扫出来。嗯,这种坑我见得太多了。
另外要注意权限控制。密钥文件权限设为 600,属主是运行服务的用户,其他用户一概不可读。如果你用 Vault 或 KMS,别忘了对访问密钥的 API 做认证和审计。
4.3 密钥轮换:定期更新策略
密钥用得越久,泄露风险越大。所以得定期换。但轮换不是简单生成一个新密钥就完事——你得考虑旧密钥加密的数据怎么办。
常用的策略是密钥版本化。每个密钥有一个版本号,新数据用最新版本加密,旧数据保留旧版本密钥用于解密。我建议轮换周期不要超过一年,对于高敏感接口,可以缩短到 90 天。
# 伪代码:密钥轮换流程
# 1. 生成新密钥并赋予版本 v2
v2_key = kms.generate_key("my-key", version=2)
# 2. 更新应用配置,标记 v2 为当前加密密钥
app_config.set_encryption_key("my-key", version=2)
# 3. 后台任务:用 v2 重新加密旧数据(渐进式)
for record in get_old_records(decrypted_with_v1):
re_encrypt(record, v2_key)
# 4. 确认所有旧数据迁移完成后,废弃 v1 密钥
kms.disable_key_version("my-key", version=1)
4.4 泄露风险与应对
密钥泄露是迟早要考虑的事,别幻想永远不会发生。泄露可能来自日志文件、备份数据、内存转储、甚至是开发者误操作把密钥贴到 GitHub 上。
应对策略分三步走:
- 检测:监控密钥的异常使用,比如短时间内大量解密请求、来自异常 IP 的解密操作。加日志告警。
- 响应:立即禁用泄露的密钥,并轮换所有受影响的密钥。如果是对称密钥,还要考虑是否暴露了已加密的数据。
- 根因分析:找到泄露渠道并修复。比如,是不是日志框架不小心打印了密钥?那就加脱敏过滤。
另外,考虑使用密钥托管和分隔机制。比如用信封加密(Envelope Encryption):数据用数据加密密钥(DEK)加密,DEK 再用主密钥(KEK)加密存储。这样即使 DEK 泄露,没有 KEK 也无法解密历史数据。KEK 放在 HSM 或 KMS 里,权限严格控制。
最后,安全擦除。废弃的密钥不能直接 delete 了事,要覆写或物理销毁。软件层面可以用 javax.crypto.EncryptedPrivateKeyInfo 配合安全擦除方法,硬件层面 HSM 通常提供密钥销毁指令。