4. 哈希与消息认证:从指纹到信任链
聊到加密传输,很多人第一反应就是「把数据锁起来」。但有个问题常被忽略——你怎么知道收到的数据没被人动过?哪怕它看起来完整无缺。
这就是哈希和消息认证要解决的事。说白了,哈希就是给数据算个「数字指纹」。指纹变了,数据肯定有问题。我在项目中见过太多人只关注加密算法,却忽略了完整性校验,结果被中间人篡改了数据都不知道。
4.1 哈希函数:MD5 与 SHA 家族
哈希函数有个特点:输入任意长,输出固定短。而且从输出反推输入,几乎不可能。这就像把一头大象塞进一个鞋盒,然后盖上盖子——你再也拿不出大象,但鞋盒的形变能证明里面确实有大象。
核心特性:
- 单向性:从哈希值无法还原原文
- 抗碰撞性:很难找到两个不同输入产生相同哈希值
- 雪崩效应:输入改一个比特,输出天翻地覆
4.1.1 MD5:曾经的王者,现在的弃子
MD5 输出 128 位,算起来快。但它的碰撞漏洞已经被公开利用。我记得 2017 年有个项目,客户坚持用 MD5 做文件校验。我给他们演示了用工具生成两个不同文件但 MD5 相同,他们当场就改了方案。
警告:MD5 和 SHA-1 已被证明不安全,严禁用于安全场景。如果你还在用,赶紧换。
4.1.2 SHA 家族:从 1 到 3 的进化
| 算法 | 输出长度 | 安全状态 | 我的建议 |
|---|---|---|---|
| SHA-1 | 160 位 | 已攻破 | 别用 |
| SHA-256 | 256 位 | 安全 | 首选 |
| SHA-384 | 384 位 | 安全 | 高安全场景 |
| SHA-512 | 512 位 | 安全 | 性能允许时 |
| SHA-3 | 可变 | 安全 | 新项目可考虑 |
我个人习惯用 SHA-256。它够快,也够安全。SHA-512 虽然更安全,但计算开销大,移动端上会有点吃力。
4.2 HMAC:带密钥的哈希
普通哈希有个问题:谁都能算。如果攻击者篡改了数据和哈希值,接收方根本发现不了。HMAC 解决了这个问题——它把密钥混进哈希计算里。
说白了,HMAC = Hash(密钥 || Hash(密钥 || 消息))。这个双重计算的设计,能抵抗长度扩展攻击。我以前踩过这个坑,直接用 Hash(密钥 || 消息) 做认证,结果被人用长度扩展攻击绕过去了。
实战建议:HMAC 的密钥长度不要小于哈希输出长度。比如 HMAC-SHA256,密钥至少 32 字节。
// Python 示例:HMAC-SHA256
import hmac
import hashlib
key = b'my-secret-key-32-bytes-long!!'
message = b'转账 1000 元给 Alice'
hmac_value = hmac.new(key, message, hashlib.sha256).hexdigest()
print(hmac_value)
// 输出: 6a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
4.3 数字签名:DSA 与 ECDSA
HMAC 有个局限:通信双方必须共享同一个密钥。如果 A 给 B 发消息,B 能验证消息是 A 发的吗?不能,因为 B 自己也能用同样的密钥生成 HMAC。
数字签名解决了这个问题。它用私钥签名,公钥验证。私钥只有签名者持有,所以签名不可抵赖。
4.3.1 DSA:经典的数字签名算法
DSA 基于离散对数难题。它的签名由两个整数 (r, s) 组成。验证时用公钥和消息算出 r',跟签名里的 r 比对。
DSA 有个坑:随机数 k 绝对不能重复使用。如果两次签名用了同一个 k,私钥就能被算出来。我在审计代码时见过这种事故,那哥们儿用固定随机数种子,结果私钥直接暴露了。
4.3.2 ECDSA:更小更快
ECDSA 是 DSA 的椭圆曲线版本。同样的安全强度下,ECDSA 的密钥更短、计算更快。256 位的 ECDSA 跟 3072 位的 DSA 安全性相当。
| 特性 | DSA | ECDSA |
|---|---|---|
| 密钥长度 (128 位安全) | 3072 位 | 256 位 |
| 签名长度 | 512 位 | 512 位 |
| 计算速度 | 较慢 | 快 |
| 推荐曲线 | — | P-256, secp256k1 |
我个人倾向 ECDSA。它在 IoT 设备、移动端上表现很好。不过要注意曲线选择,有些曲线有后门嫌疑,比如 NIST 的 P-256 虽然广泛使用,但有些人担心它的参数生成过程不透明。
4.4 证书链验证:信任的传递
数字签名解决了「谁签的」问题,但没解决「公钥是谁的」。你收到一个公钥,怎么确定它真的是对方的?
证书链就是答案。CA(证书颁发机构)给网站签发证书,你的浏览器信任 CA,所以也信任 CA 签发的证书。这就是信任链。
证书链结构:
- 根证书:自签名,预装在操作系统或浏览器中
- 中间证书:由根证书签发,用于分担风险
- 终端证书:由中间证书签发,绑定具体域名
验证过程是这样的:
- 从终端证书开始,提取签发者信息
- 用签发者的公钥验证终端证书的签名
- 重复上一步,直到找到根证书
- 检查根证书是否在信任列表中
- 检查证书是否过期、是否被吊销
我曾经遇到过一个生产事故:某个服务的证书链不完整,只发了终端证书,没发中间证书。Android 设备能自动补全,但 iOS 设备直接报错。折腾了两天才找到原因。
注意:证书吊销检查(CRL/OCSP)不能忽略。2019 年有个知名 CDN 厂商因为 OCSP 响应超时,导致大量用户访问失败。现在很多浏览器会「软失败」——如果检查不到吊销信息,默认放行。这其实是个安全隐患。
4.5 知识体系总览
下面这张图把本章的核心逻辑串起来了。从哈希到 HMAC,再到数字签名和证书链,每一步都在解决上一层的遗留问题。
你看,从哈希到证书链,每一步都在解决前一步的短板。哈希保证完整性,HMAC 加入认证,数字签名实现不可抵赖,证书链建立信任基础。少了任何一环,整个安全体系都有漏洞。
嗯,这一章的内容就到这儿。记住一句话:加密解决机密性,哈希解决完整性,签名解决真实性,证书解决信任问题。四者缺一不可。