签名给了我们认证与不可否认。但"你凭什么信任一个从没见过的网站的公钥"——这个问题,把我们引向整个互联网的信任骨架。
第 16、17 章我们看到,私钥能产出"只有持有者能产出、人人可验证"的凭据。这就是数字签名——它同时兑现了第 1 章四大目标里最后两个:认证(确实是你签的)和不可否认(你没法抵赖)。这一章讲清签名怎么用对,以及它如何撑起整个互联网的信任。
第 12 章的 HMAC 也能保证完整性和认证,和签名有什么不同?关键在密钥的对称性:
| MAC(如 HMAC) | 数字签名 | |
|---|---|---|
| 用什么密钥 | 一把共享密钥 | 私钥签,公钥验 |
| 谁能生成 | 任何持有共享密钥的人 | 只有私钥持有者 |
| 谁能验证 | 同样需要共享密钥 | 任何人(公钥是公开的) |
| 不可否认? | 不能(双方都能生成,可互赖) | 能(只有你能签) |
这个差别是本质的。MAC 里通信双方共享密钥,所以 Bob 无法向第三方证明"这条消息是 Alice 发的"——因为 Bob 自己也能造出同样的 MAC。签名不同:全世界只有 Alice 有私钥,任何人用她的公钥都能验证,于是签名成了可对外证明的凭据。这正是它能用于合同、证书、软件发布的原因。
一个关键工程细节:实际签名时,我们从不直接签消息本身,而是签它的哈希:
两个原因:①效率——公钥运算慢,消息可能几百 MB,而哈希永远是固定的 32 字节;②结构需要——签名算法只能处理固定大小的输入。这也让第 10、11 章的伏笔在此闭环:签名的安全性直接取决于哈希的抗碰撞性。如果哈希能被造碰撞(像 MD5、SHA-1),攻击者就能让你签一份无害文件,而这枚签名对另一份恶意文件同样有效(第 11 章说过的招)。所以现代签名一律配 SHA-256 起步。
下面用浏览器原生 ECDSA(P-256 曲线,即第 17 章的椭圆曲线签名)。流程:生成密钥对 → 用私钥签消息 → 用公钥验证(通过)→ 然后你改动消息哪怕一个字符,再验证(失败)。
第 17 章末尾埋的雷,现在引爆。ECDSA 每次签名都要用一个一次性随机数 k(nonce)。它必须每次不同、且不可预测。一旦违反,后果是灾难性的——不是泄露一条消息,而是泄露私钥本身:
2010 年,黑客组织 fail0verflow 发现索尼 PlayStation 3 的代码签名用的 ECDSA,那个本该随机的 k 被写成了一个固定常数。于是每个签名的 k 都一样——攻击者拿两个官方签名一算,解出了索尼的主签名私钥,从此可以签署任意代码让 PS3 当作官方固件运行。整台主机的信任根基,毁于一个没做随机的 nonce。同样的错误也曾让一些早期比特币钱包被盗——安卓某版本 SecureRandom 的缺陷导致 k 重复,私钥外泄,币被卷走(第 22 章还会细说)。
既然随机数这么难做对,现代方案干脆不用随机数:RFC 6979 确定性 ECDSA 用"私钥 + 消息哈希"通过 HMAC 推导出 k——保证每条不同消息的 k 都不同、不可预测,又完全可复现、不依赖随机源。Ed25519(第 17 章提过)天生就是确定性的,并且整体设计成"难以用错"。新项目优先 Ed25519;用 ECDSA 就确保库实现了 RFC 6979。这条经验呼应全书的主题:把容易出错的环节从系统里彻底移除,好过指望每个人都做对。
现在有个更深的问题。签名验证需要签名者的公钥。当你的浏览器连上 bank.com,银行发来一个公钥说"这是我的,用它验证我的签名"。可 Mallory(第 15 章的中间人)也能发一个公钥说同样的话。你怎么知道这个公钥真的属于银行,而不是冒充者?
这就是第 15 章 DH 中间人问题的一般形式:签名解决了"消息没被改",却没解决"这个公钥到底是谁的"。互联网的答案是一套叫 PKI(公钥基础设施)的信任委托体系:
验证时,你的设备顺着这条链一路验签:每一环的签名都用上一环的公钥来验,直到抵达那个你本来就信任的根。信任被这样层层委托下来——你不需要认识银行,只需要信任那几十个预装的根 CA,以及它们的签名。
PKI 把信任集中到了 CA 身上,于是 CA 就成了单点风险:
· CA 被攻破:2011 年 DigiNotar 被黑,签发了假的 *.google.com 证书,被用于监听伊朗用户。该 CA 随后被全球吊销、破产。
· 任何一个受信 CA 都能为任何域名签证书——你信任的是全部根 CA 里最弱的那个。
缓解手段:证书透明度(CT)要求所有证书公开记入可审计日志,便于发现异常签发;证书吊销(CRL/OCSP)处理已泄露的证书;而 App 可以用证书固定(pinning)只信任特定证书,不再信任整个 CA 体系——这正是第 21 章的主题。
公钥密码学解开了那个贯穿前三个 Phase 的死结。现在 Alice 和 Bob 可以:用 DH/ECDH 在公开信道协商密钥、用签名 + 证书确认对方身份、再用第 2 Phase 的 AEAD 加密正文。三块拼图凑齐了。
而把这三块拼图组装成一个真实协议,让你每天亿万次无感使用的,就是 TLS。下一个 Phase 从工程视角切入:我们先把一次 HTTPS 握手逐条消息拆开,然后落到 Android 平台,看这些理论如何(以及如何被错误地)变成生产代码。