核心密码学原语:哈希、HMAC 与 PRF
聊密钥派生,绕不开三个基础工具:哈希函数、消息认证码和伪随机函数。很多人觉得这些是数学课的内容,直接调库就行。但我在实际项目里吃过亏——选错了原语,整个系统的安全性就悬了。
这一节,我们把这些原语掰开揉碎,讲清楚它们是什么、怎么用、什么时候选哪个。
哈希函数:SHA-256 的硬核真相
哈希函数,说白了就是一个「数字指纹机」。你给它任意长度的数据,它吐出一个固定长度的摘要。SHA-256 的输出是 256 位,也就是 32 字节。
它有三个核心特性:
- 抗原像性:给定摘要,几乎不可能反推出原文。这叫「单向性」。
- 抗第二原像性:给定原文 A,几乎找不到另一个原文 B 使得 hash(A) = hash(B)。
- 抗碰撞性:任意找两个不同的原文,让它们的哈希值相等,计算上不可行。
嗯,这里要注意:抗碰撞性比抗第二原像性更强。SHA-256 目前在这三个特性上都没有被攻破,所以它还是业界首选。
关键认知:哈希函数是「公开的、无密钥的」。任何人都能算,算出来都一样。它不能用来做消息认证,因为没有密钥参与。
我在项目中遇到过有人用 MD5 做文件完整性校验,结果被碰撞攻击搞出了大问题。后来全部迁移到 SHA-256,再也没出过事。你想想看,一个已经被攻破的算法,就像一把生锈的锁,看着还在,其实一碰就开。
消息认证码:HMAC 的实战价值
哈希函数没有密钥,所以它不能证明「谁」产生了这个摘要。消息认证码(MAC)就是来解决这个问题的。
HMAC 是 MAC 的一种经典构造,它把哈希函数和密钥揉在一起。公式长这样:
HMAC(K, m) = H( (K' ⊕ opad) || H( (K' ⊕ ipad) || m ) )
其中 K' 是经过填充的密钥,ipad 和 opad 是固定的填充字节。这个结构保证了:
- 只有知道密钥的人才能验证消息的完整性。
- 即使哈希函数有轻微弱点,HMAC 依然安全。
我的习惯:在密钥派生场景中,HMAC 经常被用作伪随机函数的实现。HKDF 的 extract 和 expand 阶段,底层用的就是 HMAC-SHA256。这个组合经过了大量密码分析,非常可靠。
我曾经在一个 IoT 项目里,设备之间用简单的哈希做消息校验。结果中间人攻击直接伪造了消息。后来换成 HMAC,密钥存在安全元件里,问题才彻底解决。说白了,没有密钥参与的校验,就是裸奔。
伪随机函数:PRF 的本质
伪随机函数(PRF)是一个函数族。给定一个密钥和一个输入,它输出一个看起来完全随机的值。但注意,它是确定性的——同样的密钥和输入,永远得到同样的输出。
PRF 的核心要求是:
- 不可区分性:攻击者无法区分 PRF 的输出和真正的随机数。
- 密钥相关性:不同的密钥产生不同的函数行为。
在密钥派生中,PRF 的作用就是「拉伸」和「混合」。你给它一个熵源(比如密码),它给你生成一串不可预测的密钥材料。
避坑指南:我曾经看到有人直接用 SHA-256(password) 作为派生密钥。这是大错特错的。哈希函数不是 PRF,它没有密钥,而且容易受到长度扩展攻击。正确的做法是用 HMAC 或专门的 KDF 来构造 PRF。
三者的关系与选择
这三者的关系,我用一张图来展示:
如何选择?一张表说清楚
| 场景 | 推荐原语 | 原因 |
|---|---|---|
| 文件完整性校验 | SHA-256 | 不需要密钥,公开验证 |
| 消息认证(双方通信) | HMAC-SHA256 | 需要密钥,防伪造 |
| 密钥派生(KDF) | HKDF(基于 HMAC) | 需要 PRF 属性,拉伸熵 |
| 密码哈希(存储密码) | bcrypt / argon2 | 需要慢哈希,抗暴力破解 |
| 随机数生成 | PRF + 种子 | 需要不可区分性 |
我的建议:在密钥派生场景中,永远优先选择 HMAC 作为 PRF 的实现。不要自己造轮子,不要用简单的哈希拼接。HKDF 是经过严格证明的构造,直接用就好。
你想想看,如果选错了原语,就像用螺丝刀去钉钉子——不是不能用,但效率低、风险高。理解了这三个原语的本质和关系,后面的密钥派生机制就水到渠成了。