🏠 总目录📚 本教程 19 · 密钥管理 ← →
📑 本页目录(点开跳转)

19 · 密钥管理

⏱ 26 分钟 | ⭐ 真实系统 90% 的事故出在这一章


🎯 一句话

算法几乎从不被攻破,密钥管理天天出事。 Git 里的硬编码密钥、永不轮换的凭据、日志里的明文 token —— 这才是密码系统真实的死因。


🗝️ 一、密钥的分层

上方箭头表示加密保护关系:KEK 保护 DEK,DEK 保护数据。下方是独立的通信用途,不表示从 DEK 派生会话密钥。信封加密与会话密钥:两种用途分开看主密钥 KEK:加密保护 DEK数据密钥 DEK:加密业务数据实际数据会话密钥:按连接生成和销毁生成或派生方式由通信协议决定
上方箭头表示加密保护关系:KEK 保护 DEK,DEK 保护数据。下方是独立的通信用途,不表示从 DEK 派生会话密钥。

图下说明

🔑 这个分层叫「信封加密」,是云上的标准做法: 数据用 DEK 加密,DEK 用 KEK 加密后和数据存在一起。

这样分层的三个好处:


🌱 二、密钥派生:一个主密钥生出很多个

结果对照

❌ 错误:多个用途共用同一个密钥
一处泄露全线崩溃
某些组合还会互相打穿(第 17 章的密钥复用)
✅ 正确:用 KDF 从主密钥派生
import os
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes

master = os.urandom(32)          # ⭐ 主密钥就该是这样一串随机字节

def derive(master, purpose: bytes, salt: bytes = None):
    return HKDF(algorithm=hashes.SHA256(), length=32,
                salt=salt, info=purpose).derive(master)   # ⭐ info 做域分隔

enc_key = derive(master, b"myapp/v1/encryption")
mac_key = derive(master, b"myapp/v1/authentication")   # ⭐ 不同用途不同密钥

HKDF 的两个阶段:

对照

Extract:把一个【分布不均匀】的输入(如 DH 的 g^ab)

压成一个均匀的伪随机密钥 ⭐

Expand: 从它扩展出任意多个、任意长度的子密钥

⚠️ 别把用户口令直接喂给 HKDF: 口令熵太低,HKDF 很快,等于没保护。 口令必须走 Argon2id / scrypt(第 9 章)—— 那是慢的、抗 GPU 的。

   高熵输入(DH 结果、随机主密钥) → HKDF ✅
   低熵输入(用户口令)            → Argon2id ✅

🔄 三、轮换与生命周期

关键信息

为什么必须轮换:

理由 说明
限制爆炸半径 密钥泄露只影响它服役那段时间的数据
限制加密数据量 生日界!同一密钥加密的分组数不能接近 2^(n/2)(第 6 章)⭐
人员流动 离职的人可能见过旧密钥
合规要求 PCI-DSS 等标准强制要求

轮换的工程难点:

操作步骤

  1. ⭐ 关键:轮换必须支持【新旧密钥共存】的过渡期
  2. 新数据用新密钥加密
  3. 旧数据仍能用旧密钥解密
  4. 后台逐步把旧数据重加密
  5. 全部迁移完成后才销毁旧密钥
  6. 💡 所以密文里必须带【密钥版本号】⭐
  7. 形如:v2 | nonce | ciphertext | tag

💥 不带版本号的后果:轮换那天所有旧数据全部解不开 —— 这是一个非常常见的生产事故。


💥 四、真实世界的密钥泄露方式(按频率排序)

操作步骤

  1. 代码仓库里的硬编码密钥 ⭐ ← 头号杀手
  2. GitHub 上每天泄露【数千】个有效凭据
  3. 日志 / 错误信息里打印了密钥或 token
  4. 配置文件被打进容器镜像 / 前端 bundle
  5. CI/CD 的环境变量泄露到构建日志
  6. 备份文件未加密
  7. 内部人员
  8. ……很久之后才是"算法被攻破" 💀

防御清单:

措施 做什么
密钥扫描 ⭐ pre-commit hook + CI 扫描(gitleaks、truffleHog)
集中管理 Vault / AWS KMS / Azure Key Vault,代码里只有引用不含密钥
短期凭据 ⭐ 用 IAM 角色 / OIDC 换取临时凭据,根本不存长期密钥
日志脱敏 结构化日志 + 敏感字段白名单
泄露即轮换 ⚠️ 从 git 历史里删掉不等于没泄露——必须假设已泄露并轮换 ⭐

🔑 最后一条最重要,也最常被误解: 很多人发现密钥进了 git 之后,git filter-branch 删掉历史就以为没事了。 但那个 commit 可能已经被 clone、被 fork、被爬虫抓走了。 唯一正确的反应是:立刻轮换那把密钥。


🏦 五、HSM 与 KMS

关键信息

云 KMS 的信任边界问题:

关键信息


🤝 六、多方持有一个密钥

方案 做法 用在哪
Shamir 秘密共享 ⭐ 把密钥拆成 n 份,任意 k 份能恢复,k−1 份得不到任何信息 根 CA 密钥、冷钱包
门限签名 k 个人协作签名,私钥从未被完整重建过 ⭐ 机构级加密货币托管
多重签名 n 个独立签名,需要 k 个才算数 区块链多签钱包
📐 Shamir 秘密共享怎么做到的(想看再点)

核心思路:k 个点唯一确定一条 k−1 次多项式。

💡 注意它和第 3 章 OTP 一样是信息论安全的 —— 不依赖任何计算假设。

💡 门限签名 vs 多重签名的关键区别: 多签是"n 个签名都放链上"(占空间、暴露了策略); 门限签名产出的是一个普通签名,外界看不出它是多方协作的 ⭐。


🔗 和站内其他章的关系

相关的地方 和这一章的关系
第 15 章 前向保密 会话密钥用完即删
第 11 章 加密和 MAC 用不同密钥 用 HKDF 派生
第 6 章 生日界 密钥轮换的量化依据 ⭐
第 9 章 Argon2 低熵输入必须用它
第 4 章 熵源 密钥生成的前提
智能体工程 15 · 安全:沙箱与提示注入 ⭐ 那边的硬边界「凭据不进沙箱」,正是本章 KEK/DEK 分层在 Agent 架构上的落地:让被提示注入骗到的模型即使照做,也够不着密钥本身
模型上线之后 · 合规审计与模型卡 轮换周期、谁能取密钥、取过的记录——这些全是审计要查的条目;密钥管理做不好,合规那一关直接过不去 ⭐
Claude 资料库 · 触达生产系统 「别自建密钥管理」的工程落地版:用托管 Vault 存取和刷新 token——和本章 HSM / KMS 那一节是同一个结论

✅ 检查点

  1. 什么是信封加密?它有哪三个好处?
  2. 为什么不同用途必须用不同密钥?怎么做到?
  3. HKDF 的两个阶段分别做什么?
  4. 为什么用户口令不能直接喂给 HKDF?该用什么?
  5. 必须轮换密钥的四个理由?
  6. 轮换的工程关键点是什么?不做会怎样?
  7. 真实世界密钥泄露的头号原因是什么?
  8. 密钥进了 git 之后,删掉历史就够了吗?
  9. Shamir 秘密共享的原理是什么?为什么 k−1 份得不到任何信息?
  10. 门限签名和多重签名的关键区别?
👀 答案
  1. 数据用 DEK 加密,DEK 用 KEK 加密后和数据存在一起。好处:①轮换 KEK 只需重新加密很短的 DEK,不用重加密 TB 级数据 ②KEK 永不离开 HSM ③不同数据用不同 DEK,隔离爆炸半径。
  2. 因为一处泄露会全线崩溃,某些组合还会互相打穿。用 HKDF 派生,把用途字符串放进 info 参数做域分隔。
  3. Extract:把分布不均匀的输入(如 DH 的 g^ab)压成均匀的伪随机密钥;Expand:从它扩展出任意多个任意长度的子密钥。
  4. 因为口令熵太低,而 HKDF 很快,等于没保护。必须用 Argon2id / scrypt——慢的、抗 GPU 的。规则:高熵输入走 HKDF,低熵输入走 Argon2id。
  5. ①限制爆炸半径 ②限制同一密钥加密的数据量(生日界)③人员流动 ④合规要求。
  6. 必须支持新旧密钥共存的过渡期:新数据用新密钥,旧数据仍能解密,后台逐步重加密,全部迁移完才销毁旧密钥。所以密文必须带密钥版本号。不做的话轮换那天所有旧数据全部解不开。
  7. 代码仓库里的硬编码密钥。GitHub 上每天泄露数千个有效凭据。
  8. 不够。那个 commit 可能已被 clone、fork、爬虫抓走。唯一正确的反应是立刻轮换那把密钥。
  9. k 个点唯一确定一条 k−1 次多项式。秘密作常数项,每人拿一个点,k 个点插值还原后 f(0) 就是秘密。k−1 个点时剩下的自由度让 f(0) 可以是任何值,所以是信息论意义上零泄露(和 OTP 一样不依赖计算假设)。
  10. 多签是"n 个签名都放链上"(占空间、暴露策略);门限签名产出一个普通签名,外界看不出它是多方协作的,且私钥从未被完整重建过。

🛑 可以停在这里

⚡ 走神救援

⭐⭐ 算法几乎从不被攻破,密钥管理天天出事。

分层(信封加密):主密钥锁在硬件里永不导出,用它加密数据密钥。⭐ 好处是轮换主密钥只需重新加密那一小段数据密钥,不用动 TB 级数据。

派生要做域分隔——⭐ 不同用途必须是不同密钥。⚠️⭐ 但用户口令不能走这条路:熵太低而且它太快——低熵口令要走专门的慢哈希,高熵材料才走密钥派生函数。

轮换四个理由里有一个纯技术的:⭐ 限制同一把密钥加密的数据量。⭐ 工程上的关键是支持新旧共存的过渡期,所以密文必须带版本号——💥 不带的话,轮换那天旧数据全部解不开。

⭐⭐ 真实泄露方式按频率排,排在最前面的是「代码仓库里硬编码」——公开仓库每天泄露数千个有效凭据;接着是日志打印、配置进镜像或前端包、CI 环境变量、备份没加密、内部人员……⭐ 算法被破排在很后面。

⚠️⭐ 从 git 历史里删掉不等于没泄露:那个 commit 可能已经被克隆、被 fork、被爬虫抓走了——⭐ 唯一正确的反应是立刻轮换。

防御里最彻底的一条是 ⭐ 短期凭据:换成临时凭据之后,你根本不存长期密钥。 云托管方便,但你必须信任云厂商——⭐ 最强的缓解是客户端加密:上传前就加密,云只存密文。

⭐ 多方持有两件事:秘密分享(少一份时秘密可以是任何值,⭐ 所以是信息论意义上的零泄露)和门限签名——⭐ 私钥从未被完整重建过,而且产出的是普通签名,外界看不出是多方协作。

下一节 👉 20-区块链.md

打卡记录保存在你的浏览器里,首页能看到总进度