1. 密钥派生基础:从密码学原语到实际应用场景
大家好,我是这门课的主讲人。今天咱们聊聊密钥派生——这个听起来有点学术,但实际上每天都在用的东西。
你可能会问:为什么需要密钥派生?直接拿用户密码当密钥不行吗?嗯,我刚开始做密码学工程时也这么想过。直到有一次,我在一个项目中看到用户密码直接被当成 AES 密钥使用……那场面,怎么说呢,就像把家门钥匙挂在门外墙上。
1.1 密码学原语:密钥从哪来?
先看几个基础概念。密码学里,密钥的来源大致分三类:
| 来源类型 | 典型例子 | 特点 |
|---|---|---|
| 用户输入 | 密码、PIN码、口令 | 低熵、可预测、长度不固定 |
| 硬件生成 | HSM、TPM、安全芯片 | 高熵、不可导出、成本高 |
| 协议派生 | DH协商、KDF计算 | 可控、可重复、可分层 |
说白了,用户密码的熵值通常只有 20-30 比特。而 AES-128 需要 128 比特的均匀随机密钥。这中间的差距,就是密钥派生要解决的问题。
核心观点:密钥派生不是锦上添花,而是安全工程的刚需。没有它,你的加密系统就像用生日当密码的保险柜。
1.2 为什么不能直接用原始密钥材料?
我见过不少团队踩过这个坑。直接使用原始密钥材料,至少有三个致命问题:
- 熵不匹配:用户密码只有几十比特熵,直接当密钥会被暴力破解轻松拿下
- 结构泄露:原始密钥材料往往有固定模式(比如密码开头大写、结尾数字),攻击者可以利用这些模式
- 重用风险:同一个密码用在多个地方,一个泄露全部完蛋
举个例子。我曾经审计过一个物联网项目,他们把设备序列号直接当成加密密钥。结果呢?序列号是递增的,攻击者拿到一个设备就能推算出所有设备的密钥。这就是典型的「原始密钥材料直接使用」灾难。
1.3 密钥派生函数(KDF)的核心职责
密钥派生函数,英文叫 Key Derivation Function,简称 KDF。它的工作就是:把任意长度的输入,变成固定长度、高熵、无结构的密钥。
一个合格的 KDF 必须做到:
- 单向性:从输出无法反推输入
- 均匀性:输出比特分布均匀,没有统计偏差
- 雪崩效应:输入改一比特,输出完全变样
- 抗碰撞:不同输入产生相同输出的概率极低
我的经验:判断一个 KDF 好不好,最简单的测试就是:把输入改一个字符,看输出是不是完全不一样。如果只有几个比特变化,赶紧换算法。
1.4 实际应用场景:你每天都在用
密钥派生不是实验室里的玩具。它无处不在:
- Wi-Fi 密码:你输入的路由器密码,通过 PBKDF2 派生出 WPA2/WPA3 的成对主密钥
- HTTPS:TLS 握手过程中,通过 HKDF 从预主密钥派生出会话密钥
- 加密货币钱包:助记词通过 BIP32/BIP39 派生出一系列子密钥
- 全盘加密:BitLocker、FileVault 用用户口令派生出磁盘加密密钥
你看,密钥派生不是可选项,而是现代密码系统的标配。
1.5 密钥派生树:为什么需要分层?
单次派生够用吗?大多数场景下不够。为什么呢?
想象一下:你的应用需要加密数据、签名消息、认证用户。如果所有操作都用同一个密钥,一旦泄露,全线崩溃。更合理的做法是:从一个主密钥,派生出多个子密钥,每个子密钥只负责一件事。
这就是密钥派生树的由来。它像一棵树:
- 根节点:主密钥(高熵、严格保护)
- 分支节点:中间派生密钥(按用途或路径划分)
- 叶子节点:实际使用的密钥(加密、签名、认证等)
我在做金融系统时,就遇到过密钥管理混乱的问题。所有业务线共用一个密钥,结果一条业务线被攻破,整个系统都得换密钥。后来我们引入了密钥派生树,每条业务线有自己的派生路径,隔离性好了很多。
注意:密钥派生树不是越深越好。每层派生都有计算开销,而且深度过大会增加密钥管理的复杂度。一般 3-5 层就够用了。
1.6 本章知识体系总览
下面这张图,概括了密钥派生的核心逻辑:
从这张图可以看到:原始密钥材料经过 KDF 处理后,变成主密钥;主密钥再通过派生树,生成多个用途不同的子密钥。每个子密钥各司其职,互不干扰。
1.7 常见 KDF 算法对比
市面上常见的 KDF 算法,各有千秋:
| 算法 | 设计目标 | 抗暴力破解 | 适用场景 |
|---|---|---|---|
| PBKDF2 | 慢哈希 + 加盐 | 中等(可调迭代次数) | 密码存储、Wi-Fi 密钥派生 |
| HKDF | 提取 + 扩展 | 低(设计目标不是慢) | TLS 会话密钥、协议派生 |
| Argon2 | 内存硬 + 时间硬 | 高(抗 GPU/ASIC) | 密码哈希、高安全场景 |
| scrypt | 内存硬 | 较高 | 加密货币挖矿、密码存储 |
我的建议:如果是新项目,优先考虑 Argon2id。它在抗 GPU 和 ASIC 方面做得最好。如果是兼容旧系统,PBKDF2 也够用,但迭代次数别低于 100000。
1.8 避坑指南:我踩过的那些坑
最后分享几个我亲身经历过的教训:
- 忘记加盐:曾经有个同事直接用密码做 KDF 输入,不加盐。结果两个用户密码相同,派生出的密钥也相同。攻击者只要破解一个,就知道另一个。
- 迭代次数太低:早期一个项目用 PBKDF2 只迭代 1000 次。当时觉得够快了,后来发现 GPU 破解速度惊人。现在我都用 100000 次起步。
- 混淆 KDF 和哈希:有人用 SHA-256 直接当 KDF 用。SHA-256 是哈希函数,不是 KDF。它没有加盐机制,也没有可调计算成本。
嗯,这些坑说起来都是泪。但踩过之后,你就知道密钥派生这件事,真不能马虎。