怎样做到:即使攻击者今天攻破了你的手机,也读不了你昨天发的消息,也读不了你明天恢复安全后的消息。
Signal 协议保护着 Signal、WhatsApp、以及无数 App 的私聊。它把我们学过的 ECDH、KDF、AEAD 组合成一台不断"自我更新密钥"的机器,达成了单靠 TLS 做不到的安全等级。它是密码学"正确组合"艺术的巅峰之一。
TLS(第 19 章)保护的是"你 ↔ 服务器"这一段。但聊天场景里,消息要经过服务器中转给对方——TLS 解密到了服务器就变回明文,服务器(以及攻破它的人、拿传票来的人)全看得到。
端到端加密(E2EE)把信任面缩到极致:消息在你的设备上加密,只在对方设备上解密,中间的服务器全程只看到密文。服务器只负责转发一堆它读不懂的字节。这正是第 1 章"在不可信环境建立信任"的极端形态——连帮你传话的人都不信。
第一个难题:Diffie-Hellman(第 15 章)需要双方同时在线交换公钥。可你给朋友发消息时,他手机可能关着。怎么和一个离线的人做密钥协商?
Signal 的 X3DH(三重 Diffie-Hellman)这样解决:每个用户提前把一批公钥(身份钥、签名的预共享钥、一次性预共享钥)上传到服务器。你要发消息时,从服务器取下对方的这些公钥,用自己的私钥和它们做多次 ECDH,把结果一起喂进 KDF,算出一把初始共享密钥——全程不需要对方在线。对方上线后,用你附带的公钥做同样的计算,得到相同的密钥。
做多次 DH(而非一次)是为了同时绑定长期身份(认证,防第 15 章的中间人)和临时性(前向保密)。这又是那句老话:密钥协商 + 身份认证必须一起做对。
X3DH 只给了初始密钥。真正的精华是接下来的 双棘轮(Double Ratchet)——它让密钥每发一条消息就前进一步、永不回头("棘轮"就是只能单向转动的齿轮)。它由两个棘轮咬合而成:
这套机制带来两个惊人的性质:
| 性质 | 含义 | 靠哪个棘轮 |
|---|---|---|
| 前向保密 Forward Secrecy | 今天密钥泄露,读不了过去的消息(旧消息钥已销毁,且倒推不出) | 对称棘轮(KDF 单向) |
| 后泄露安全 Post-Compromise Security | 今天被攻破,只要攻击者错过一轮,系统能自愈,读不了未来的消息 | DH 棘轮(注入新随机性) |
下面的 Demo 把双棘轮可视化。每发一条消息,链钥经 KDF 前进一格并派生出消息钥;消息钥用完即焚(变灰)。当发言方切换,DH 棘轮转动,注入新的 DH 输出,开一条新链。试试"模拟今天被攻破":你会看到攻击者拿到当前状态,却依然打不开已销毁的历史消息。
第 19 章说 TLS 1.3 有前向保密,粒度是每次会话一把密钥。双棘轮把粒度细化到每一条消息,并额外提供了 TLS 没有的"后泄露自愈"。代价是复杂度和状态管理(要处理乱序、丢失的消息)。这是一个典型的工程权衡:更强的安全属性,换取更复杂的实现——而复杂正是 bug 的温床,所以你永远不该自己实现 Signal 协议,用 libsignal。
Signal 里的"安全号码/验证码"功能,让你和联系人当面(或通过其它渠道)核对一串数字——它其实是双方身份公钥的指纹。这是在手动堵第 15 章那个永恒的漏洞:密钥协商能防窃听,但"对面公钥到底是不是本人"仍需带外确认。Signal 用它替代了第 18 章的 CA 体系——不依赖中心化机构,而让用户自己做信任锚。密码学绕了一整本书,又回到了那个最根本的问题:你怎么知道钥匙是对的人的?
Signal 把我们学过的经典密码学组合到了极致。但这一切——ECDH、ECDSA、RSA——都站在同一个假设上:某些数学问题算不回去。如果有一天,一台机器能瞬间解开离散对数和大数分解呢?最后一章,我们直面这个幽灵,以及另一种"证明我知道却不告诉你"的魔法。