密语 · CIPHER | 目录 CH.18 / 26
绝密
Phase 4 · 公钥革命

数字签名与信任链

签名给了我们认证与不可否认。但"你凭什么信任一个从没见过的网站的公钥"——这个问题,把我们引向整个互联网的信任骨架。

阅读 ~16 分钟 前置 第 16、17 章 Demo ECDSA 签名/验签台

第 16、17 章我们看到,私钥能产出"只有持有者能产出、人人可验证"的凭据。这就是数字签名——它同时兑现了第 1 章四大目标里最后两个:认证(确实是你签的)和不可否认(你没法抵赖)。这一章讲清签名怎么用对,以及它如何撑起整个互联网的信任。

一、签名 vs MAC:都保完整,区别在"谁能验"

第 12 章的 HMAC 也能保证完整性和认证,和签名有什么不同?关键在密钥的对称性:

MAC(如 HMAC)数字签名
用什么密钥一把共享密钥私钥签,公钥验
谁能生成任何持有共享密钥的人只有私钥持有者
谁能验证同样需要共享密钥任何人(公钥是公开的)
不可否认?不能(双方都能生成,可互赖)(只有你能签)

这个差别是本质的。MAC 里通信双方共享密钥,所以 Bob 无法向第三方证明"这条消息是 Alice 发的"——因为 Bob 自己也能造出同样的 MAC。签名不同:全世界只有 Alice 有私钥,任何人用她的公钥都能验证,于是签名成了可对外证明的凭据。这正是它能用于合同、证书、软件发布的原因。

二、先哈希,再签名

一个关键工程细节:实际签名时,我们从不直接签消息本身,而是签它的哈希:

签名 = Sign(私钥, Hash(消息))

两个原因:①效率——公钥运算慢,消息可能几百 MB,而哈希永远是固定的 32 字节;②结构需要——签名算法只能处理固定大小的输入。这也让第 10、11 章的伏笔在此闭环:签名的安全性直接取决于哈希的抗碰撞性。如果哈希能被造碰撞(像 MD5、SHA-1),攻击者就能让你签一份无害文件,而这枚签名对另一份恶意文件同样有效(第 11 章说过的招)。所以现代签名一律配 SHA-256 起步。

三、亲手签一次、验一次、再篡改一次

下面用浏览器原生 ECDSA(P-256 曲线,即第 17 章的椭圆曲线签名)。流程:生成密钥对 → 用私钥签消息 → 用公钥验证(通过)→ 然后你改动消息哪怕一个字符,再验证(失败)。

DEMO ECDSA 签名 / 验签台
未生成
签名后,试着在消息框里改一个字母再点"验证"——签名立刻失效。这就是"认证 + 完整性":签名把消息内容和签名者身份牢牢绑在一起。

四、ECDSA 的命门:那个一次性随机数

第 17 章末尾埋的雷,现在引爆。ECDSA 每次签名都要用一个一次性随机数 k(nonce)。它必须每次不同、且不可预测。一旦违反,后果是灾难性的——不是泄露一条消息,而是泄露私钥本身:

索尼 PS3:把常数当随机数

2010 年,黑客组织 fail0verflow 发现索尼 PlayStation 3 的代码签名用的 ECDSA,那个本该随机的 k 被写成了一个固定常数。于是每个签名的 k 都一样——攻击者拿两个官方签名一算,解出了索尼的主签名私钥,从此可以签署任意代码让 PS3 当作官方固件运行。整台主机的信任根基,毁于一个没做随机的 nonce。同样的错误也曾让一些早期比特币钱包被盗——安卓某版本 SecureRandom 的缺陷导致 k 重复,私钥外泄,币被卷走(第 22 章还会细说)。

解药:确定性 nonce

既然随机数这么难做对,现代方案干脆不用随机数:RFC 6979 确定性 ECDSA 用"私钥 + 消息哈希"通过 HMAC 推导出 k——保证每条不同消息的 k 都不同、不可预测,又完全可复现、不依赖随机源。Ed25519(第 17 章提过)天生就是确定性的,并且整体设计成"难以用错"。新项目优先 Ed25519;用 ECDSA 就确保库实现了 RFC 6979。这条经验呼应全书的主题:把容易出错的环节从系统里彻底移除,好过指望每个人都做对。

五、信任链:你凭什么信任一个公钥?

现在有个更深的问题。签名验证需要签名者的公钥。当你的浏览器连上 bank.com,银行发来一个公钥说"这是我的,用它验证我的签名"。可 Mallory(第 15 章的中间人)也能发一个公钥说同样的话。你怎么知道这个公钥真的属于银行,而不是冒充者?

这就是第 15 章 DH 中间人问题的一般形式:签名解决了"消息没被改",却没解决"这个公钥到底是谁的"。互联网的答案是一套叫 PKI(公钥基础设施)的信任委托体系:

  1. 证书:把"公钥"和"身份(域名)"绑在一起的一份文件,由可信第三方签名背书。证书本质是"某某权威签名保证:这个公钥属于 bank.com"。
  2. CA(证书颁发机构):受信任的第三方(如 Let's Encrypt、DigiCert)。它验证你确实拥有某域名后,用它的私钥给你的证书签名。
  3. 信任链:银行证书由中间 CA 签,中间 CA 由根 CA 签。根 CA 的公钥预装在你的操作系统/浏览器里——这是信任的终点,叫信任锚(trust anchor)
根 CA(预装信任)
  └─ 签名 → 中间 CA
      └─ 签名 → bank.com 的证书(内含 bank.com 的公钥)

验证时,你的设备顺着这条链一路验签:每一环的签名都用上一环的公钥来验,直到抵达那个你本来就信任的根。信任被这样层层委托下来——你不需要认识银行,只需要信任那几十个预装的根 CA,以及它们的签名。

这套体系的脆弱点

PKI 把信任集中到了 CA 身上,于是 CA 就成了单点风险:
· CA 被攻破:2011 年 DigiNotar 被黑,签发了假的 *.google.com 证书,被用于监听伊朗用户。该 CA 随后被全球吊销、破产。
· 任何一个受信 CA 都能为任何域名签证书——你信任的是全部根 CA 里最弱的那个。
缓解手段:证书透明度(CT)要求所有证书公开记入可审计日志,便于发现异常签发;证书吊销(CRL/OCSP)处理已泄露的证书;而 App 可以用证书固定(pinning)只信任特定证书,不再信任整个 CA 体系——这正是第 21 章的主题。

Phase 4 小结:革命完成

公钥密码学解开了那个贯穿前三个 Phase 的死结。现在 Alice 和 Bob 可以:用 DH/ECDH 在公开信道协商密钥、用签名 + 证书确认对方身份、再用第 2 Phase 的 AEAD 加密正文。三块拼图凑齐了。

而把这三块拼图组装成一个真实协议,让你每天亿万次无感使用的,就是 TLS。下一个 Phase 从工程视角切入:我们先把一次 HTTPS 握手逐条消息拆开,然后落到 Android 平台,看这些理论如何(以及如何被错误地)变成生产代码。

本章要点

  • 签名用私钥生成、公钥验证,提供认证 + 不可否认(MAC 因共享密钥无法不可否认)。
  • 实际签的是消息哈希,故签名安全性取决于哈希抗碰撞性(MD5/SHA-1 不可用)。
  • ECDSA 的一次性 nonce 若重复/可预测会直接泄露私钥(PS3、比特币事故);用 RFC 6979 或 Ed25519。
  • 证书 + CA + 信任链(PKI)回答"公钥属于谁";信任集中到 CA,靠 CT、吊销、pinning 缓解风险。