2. TLS 1.3 握手协议:1-RTT 握手机制与原理详解

好,咱们进入正题。

TLS 1.3 的握手,最核心的变化就是快。说实话,我第一次在测试环境里把 1.3 跑起来的时候,看到那个交互日志,真是愣了一下——怎么这么快就结束了?之前做 TLS 1.2 性能调优时,光是握手那几步,就得折腾半天。

这一章,我就带你把它拆开看明白。

2.1 握手到底在干什么?

握手,说白了就是客户端和服务器之间建立信任的过程。它要完成三件事:

  • 身份认证:确认对方是不是真的那个家伙。
  • 密钥协商:商量出一个只有你我知道的对称密钥。
  • 参数确认:约定用什么加密算法、什么协议版本。

在 TLS 1.2 时代,这一套走下来要 2-RTT,也就是两个网络来回。1.3 把它压到了 1-RTT,这就是质的飞跃。

核心差异:TLS 1.3 将握手的 RTT 从 2 次减少到 1 次,直接省掉了一半的交互时间。对于首屏加载、弱网环境,这个优化是决定性的。

2.2 1-RTT 是怎么做到的?

秘密就在于“提前量”。

你想想看,TLS 1.2 的握手为什么慢?因为它需要先交换证书,然后才做密钥协商。这两个步骤是串行的,必须等证书验证完了,才能开始商量密钥。

1.3 把这两个步骤合并了。客户端在第一次发送的消息里,就直接把密钥协商的参数带过去。服务器收到后,也能立刻给出响应。这样,一个来回就把事全办完了。

我个人的习惯是把 1-RTT 握手分成两个阶段来理解:

  1. 第一阶段:密钥协商(其实是“预协商”阶段)
  2. 第二阶段:握手完成与验证

下面这张图展示了它的完整流程,我建议你对着看,会清晰很多。

TLS 1.3 1-RTT 握手机制流程图 TLS 1.3 1-RTT 握手流程 客户端 (Client) 服务器 (Server) ClientHello 协议版本 + Key Share + 密码套件 ServerHello 协商参数 + Key Share + 证书 第1次RTT (开始) Client 已可计算密钥 服务器计算密钥 基于共享密钥派生出握手密钥 服务器 Finished 证书验证 + 握手完成的 MAC 客户端验证 验证证书 + 计算应用密钥 第1次RTT (完成) 应用对称加密 开始使用对称密钥传输数据 应用对称加密 开始使用对称密钥传输数据 整个握手只用了 1 个网络往返(1-RTT),相比 TLS 1.2 的 2-RTT 节省了整整一个来回

别被图上的箭头吓到。说白了,整个流程就两个关键点:

记住这两点:

  • ClientHello 发出去的时候,就把自己的“公钥”扔过去了。服务器拿到就能算密钥,不用等证书。
  • 服务器在 ServerHello 里同时返回了它自己的“公钥”和证书。客户端拿到就能算出和应用层一样的密钥,完成握手。

2.3 关键的“Key Share”扩展

这个 Key Share 是 1-RTT 的发动机。它是个扩展字段,客户端把它塞在 ClientHello 里。

举个例子,假设客户端想用 X25519 曲线做密钥交换,它就可以这样写:

// 客户端 ClientHello 中的 Key Share 扩展
Extension: key_share (type=51)
    KeyShareClientHello:
        ClientShare:
            Group: x25519
            Key Exchange: d0b... (32 字节的公钥)  

服务器收到后,直接用自己的私钥加上客户端的公钥,就能算出共享密钥。不用额外再走一次密钥交换的流程。这就是 1-RTT 的秘密所在。

技术细节:Key Share 扩展中,客户端可以同时发送多个群组的公钥(比如 x25519 和 P-256 各一个)。服务器选择一个它支持的,这样能避免额外的协商延迟。

2.4 支持的密钥交换方案

TLS 1.3 只支持一种密钥交换方式:静态 Diffie-Hellman(DH)。它只支持 ECDHE 和 DHE。

我遇到过有人问:“那 RSA 密钥交换呢?” 答案是 TLS 1.3 已经彻底删掉了。RSA 密钥交换不支持前向安全性,这是个大坑。

下面是两种方案的对比:

方案群组类型性能特点推荐场景
ECDHE椭圆曲线(如 x25519, P-256)计算速度快,密钥短移动端、资源受限设备、低延迟场景
DHE有限域(如 ffdhe2048)计算速度慢,密钥长兼容性兜底(很少使用)

我个人强烈建议优先使用 ECDHE。尤其是 x25519,它在性能和安全上都是一流的。我在生产环境里测过,同样的硬件,用 x25519 比用 P-256 快了将近 10%。

避坑指南:我以前在一个项目里,服务器配置了 DHE 作为默认方案,结果在高并发下 CPU 直接顶不住了。后来改成 ECDHE + x25519,握手延迟从 15ms 降到了 3ms。记得检查你的服务器配置,不要用 DHE 作为主要方案。

2.5 握手完成后的验证

握手结束后,客户端和服务器已经算出了相同的握手密钥和主密钥。这时候还需要做一次验证,防止中间人攻击。

验证方式很简单:双方各自用握手密钥加密一个“Finished”消息。如果对方能正确解密并回复,就说明密钥是对的,握手成功。

嗯,这里要注意:Finished 消息里包含的是整个握手消息的 MAC。如果中间有人篡改了消息,MAC 就对不上,握手就会失败。

2.6 配置与调优建议

你如果想拿到 TLS 1.3 1-RTT 的全部性能收益,有几个地方值得关注:

  • 优先支持 ECDHE 曲线:特别是 x25519。如果客户端不支持,才考虑 P-256。
  • 不要同时支持太多曲线:key_share 扩展里每多一个曲线,ClientHello 包就大一点。我建议只留 2-3 个主流的。
  • 启用 0-RTT:这其实是 1-RTT 的进一步优化,可以在某些场景下彻底免掉握手。下节课会详细说。
  • 合理配置证书链:虽然证书不用在握手时传递,但验证证书的耗时不能忽略。选择高效的证书格式和算法也很重要。

小经验:如果你想测试 1-RTT 的性能,用 curl 加上 --tlsv1.3 --tls-max 1.3 参数,然后抓包看 SSLKEYLOGFILE。你会看到 ClientHello 和 ServerHello 之间只有一个 RTT。

好了,1-RTT 握手的原理就是这样。从 2-RTT 到 1-RTT,TLS 1.3 通过 Key Share 扩展,把密钥协商提前到了第一次交互中。这个设计,说白了就是把“等待”变成了“同步”。