4. 对称加密密钥是如何在TLS握手中协商生成的?
这个问题,我面试时几乎必问。
很多人能背出「客户端随机数 + 服务端随机数 + 预主密钥 = 主密钥 → 派生密钥」。
但坦白讲,能讲清楚 「为什么不能直接用 RSA 加密的预主密钥当对称密钥」 的人,我面了上百个,一只手数得过来。
今天咱们就掰开揉碎讲明白。
你想想看,握手里所有公钥操作,最终目的只有一个——
让客户端和服务端在不可信的信道上,安全地算出同一组对称密钥。
而且这个推导过程必须是 单向的:即便攻击者拿到了所有握手消息,也无法逆向算出最终的密钥。
4.1 密钥协商的三个原料
我用一个最简单的比喻:做蛋糕。
客户端和服务端各自往配方里加料,最后烤出同一个蛋糕。
- 客户端随机数 (Client Random) —— 32 字节,明文发给服务端。它保证每次握手的密钥都不一样。
- 服务端随机数 (Server Random) —— 32 字节,明文发给客户端。同上。
- 预主密钥 (Pre-Master Secret) —— 48 字节。这是保密的。它才是真正的基础秘密。
但在不同密钥交换算法下,预主密钥的传递方式天差地别。
4.2 传统 RSA 密钥交换(现在应该避坑)
说实话,我在 2017 年排查过一个客户问题,他们还在用 RSA 密钥交换,刚升级 TLS 1.2 就报错。我一查日志,服务端证书是 RSA 公钥,但协商的密码套件里没有 RSA key exchange——现代服务端普遍默认禁用。
嗯,这是个典型的兼容性陷阱。
RSA 的流程很简单:
- 客户端生成 48 字节的预主密钥(前 2 字节是 TLS 版本号,后面 46 字节是随机数)。
- 用服务端证书里的 RSA 公钥加密 premaster,发给服务端。
- 服务端用私钥解密,拿到完整的 premaster。
然后双方拿 Client Random + Server Random + Pre-Master Secret 按 PRF (Pseudo-Random Function) 算出一个 48 字节的 Master Secret。
Master Secret = PRF(Pre-Master Secret, "master secret",
ClientRandom + ServerRandom)[0..47]
但 RSA 交换有个致命缺陷:没有前向安全性。
如果私钥泄露,攻击者可以解密历史上所有会话的记录。
所以我个人的建议:新系统不要再使用 RSA 密钥交换。 很多合规审计(比如 PCI DSS)现在也明确要求禁用。
4.3 现代方案:DHE / ECDHE 密钥交换
这才是主流。它解决了前向安全性问题。
我记得有一次线上故障,客户从国外回放流量做安全审计,所有旧会话都能被解密,因为私钥定期轮转周期设得太长——ECDHE 配合 1 小时会话票证轮转,才把审计人员的嘴堵上。
DH 的过程你肯定熟悉:
- 客户端生成临时 DH 私钥,发公钥(Client DH Public Key)
- 服务端也生成临时 DH 私钥,发公钥(Server DH Public Key)
- 双方各自计算 DH 共享秘密——这就是 Pre-Master Secret
注意:这里的 DH 私钥是临时生成的,用完即弃。所以即使未来私钥泄露,也无法解密过去的会话。
然后 PRF 计算 Master Secret 的公式一模一样:
Master Secret = PRF(Pre-Master Secret, "master secret",
ClientRandom + ServerRandom)[0..47]
看到没?PRF 的输入里,两个随机数都是明文,只有 Pre-Master Secret 是秘密。这是整个安全的基础。
4.4 从 Master Secret 到工作密钥(密钥派生)
拿到 Master Secret 之后,并不是直接拿它加密。还得再次用 PRF 派生出多个子密钥。这在 TLS 1.2 里称为「密钥扩展」。
我面试时常问:Master Secret 长度是 48 字节,为什么不能直接当 AES-256 的密钥?
原因有二:一是需要多个密钥(加密、MAC、IV);二是不同方向要用不同密钥,防止重放。
派生的产物通常包括(以 AES-128-GCM 为例):
| 参数 | 长度 | 用途 |
|---|---|---|
| Client Write Key | 16 字节 | 客户端加密 → 服务端解密 |
| Server Write Key | 16 字节 | 服务端加密 → 客户端解密 |
| Client Write IV | 4 字节(隐式) | 用于 GCM 模式计数器初始化 |
| Server Write IV | 4 字节(隐式) | 同上 |
关键:客户端和服务端的密钥是不同的。 如果两边用同一个密钥,攻击者可以伪造数据包发送给被攻击方——这叫 反射攻击。
在 TLS 1.3 里,密钥派生做了简化。Master Secret 被直接称为 Traffic Secret,并通过 HKDF (Extract-and-Expand) 一步到位算出握手密钥和应用密钥,不再显式区分 Master Secret。但底层理念一样:
- 用随机数打散
- 用密钥协商结果做种子
- 输出不同用途的独立子密钥
4.5 核心流程图
我用一张图总结整个密钥协商的生命周期:
4.6 面试常问的坑(我踩过的那种)
- 「Master Secret 长度为什么是 48 字节?」 —— 因为 TLS 1.0/1.1 时代,密钥派生需要足够的熵,48 字节刚好覆盖多数密码套件(DES、3DES、AES-128/256 等)。现在其实可以更短,但为了兼容性没改。
- 「如果客户端随机数和服务端随机数相同会怎样?」 —— 风险很大。两边会算出一样的 Master Secret,但方向不同的工作密钥仍然不同(因为派生时有 client/server 标识)。但随机数由双方分别生成,实践中不会相同。
- 「预主密钥泄漏了会怎样?」 —— 整个会话就废了。因为两个随机数是明文,攻击者可以算出 Master Secret 和所有工作密钥。我遇到过某云厂商把 premaster 打印到日志里……嗯,那是个高危漏洞。
避坑: 我曾经在生产环境看到过这么一个问题——客户端生成 premaster 时,前 2 字节(版本号)写死为 0x0303(TLS 1.2),但在 TLS 1.3 协商时,服务端强行解读为 TLS 1.2,导致版本回滚攻击。后来我强制在 premaster 里校验版本号与协商版本一致,才通过安全审计。
4.7 小结
对称密钥的协商,本质上是一个 「公开组件 + 秘密种子 → 单向函数 → 独立子密钥」 的过程。
公开组件(随机数)防止重放;秘密种子(premaster)保证机密;PRF 保证推导不可逆;多个子密钥保证方向独立。
面试时,如果能把 PRF 的输入输出讲清楚,再随口带一个「RSA 没有前向安全性、ECDHE 推荐、密钥派生方向独立」的对比,面试官大概率会点头。