4. TLS 1.3 新特性:握手、0-RTT 与密码套件

聊到 TLS 1.3,我个人的感觉是——它把之前 TLS 1.2 里那些「看着就头疼」的握手流程彻底砍了一刀。说白了,TLS 1.3 不是为了加功能,而是为了减负担。我在项目里排查过不少 TLS 1.2 的慢连接问题,很多都是握手轮次太多导致的。TLS 1.3 的出现,很大程度上就是为了解决这个痛点。

4.1 握手流程变化:从 2-RTT 到 1-RTT

TLS 1.2 的完整握手需要 2 个往返(2-RTT),而 TLS 1.3 把它压缩到了 1 个往返(1-RTT)。怎么做到的?核心思路是:客户端在第一条消息里就把大部分信息发过去。

我们来看一下对比:

阶段 TLS 1.2(完整握手) TLS 1.3(完整握手)
第 1 次 ClientHello → ServerHello + Certificate ClientHello(含 KeyShare)→ ServerHello + Certificate + Finished
第 2 次 ClientKeyExchange + ChangeCipherSpec → Finished Finished → (应用数据)
第 3 次 (应用数据开始) —

嗯,这里要注意:TLS 1.3 的 ServerHello 之后,服务器直接就把证书和 Finished 一起发过来了。客户端收到后,只需要发一个 Finished 确认,然后双方就可以开始加密通信了。整个流程少了一个完整的往返。

关键变化:TLS 1.3 移除了 ChangeCipherSpec 消息(虽然为了兼容性,有些实现还会发一个空消息),并且把密钥协商提前到了 ClientHello 阶段。

为什么会这样?因为 TLS 1.3 使用了 预共享密钥(PSK) 和 密钥共享(KeyShare) 机制。客户端在 ClientHello 里直接带上自己的密钥份额,服务器收到后就能立刻算出主密钥,不需要像 TLS 1.2 那样先交换随机数再算。

4.2 0-RTT 原理:真正的零往返

0-RTT 是 TLS 1.3 最吸引人的特性之一。它允许客户端在第一次握手之后,再次连接时直接发送应用数据,不需要等待任何握手消息。

原理其实不复杂:

  1. 第一次连接:完成正常的 1-RTT 握手,双方协商出一个 PSK(预共享密钥)。
  2. 后续连接:客户端在 ClientHello 里带上 PSK 标识和 0-RTT 数据(已经用 PSK 加密过)。
  3. 服务器收到后:如果能验证 PSK 有效,直接解密 0-RTT 数据并处理,同时回复 ServerHello 和 Finished。

我曾经在排查一个 API 网关的延迟问题时,发现客户端每次请求都要重新握手。后来改成 TLS 1.3 + 0-RTT,延迟直接从 30ms 降到了 5ms 以下。当然,0-RTT 有个坑——重放攻击。因为服务器收到 0-RTT 数据时还没确认客户端身份,所以攻击者可以截获并重放这些数据。

避坑指南:我曾经遇到过一个支付系统,开发人员把订单提交放到了 0-RTT 数据里。结果测试环境出现重复订单,就是因为 0-RTT 数据被重放了。记住:0-RTT 只适合幂等操作,比如查询、缓存刷新,绝对不要用于下单、转账等非幂等操作。

4.3 TLS 1.3 密码套件:精简到极致

TLS 1.2 支持几十种密码套件,说实话,很多都是历史遗留的「毒瘤」。TLS 1.3 直接砍掉了所有不安全的选项,只保留了 5 个密码套件:

密码套件 密钥交换 认证 对称加密 HMAC
TLS_AES_128_GCM_SHA256 (EC)DHE 证书 AES-128-GCM SHA256
TLS_AES_256_GCM_SHA384 (EC)DHE 证书 AES-256-GCM SHA384
TLS_CHACHA20_POLY1305_SHA256 (EC)DHE 证书 ChaCha20-Poly1305 SHA256
TLS_AES_128_CCM_SHA256 (EC)DHE 证书 AES-128-CCM SHA256
TLS_AES_128_CCM_8_SHA256 (EC)DHE 证书 AES-128-CCM-8 SHA256

你想想看,TLS 1.3 的密码套件名称里甚至没有密钥交换算法,因为所有套件都强制使用 (EC)DHE。这意味着 前向安全性(PFS) 是强制性的,不再是可选项。

个人习惯:我在 Wireshark 里看 TLS 1.3 握手时,会直接过滤 tls.handshake.ciphersuite == 0x1301(对应 TLS_AES_128_GCM_SHA256)。如果看到其他套件,反而要警惕是不是配置有问题。

4.4 TLS 1.2 与 TLS 1.3 核心对比

我把几个关键差异整理成了一张表,方便你对照:

对比项 TLS 1.2 TLS 1.3
握手轮次 2-RTT(完整握手) 1-RTT(完整握手)
0-RTT 不支持 支持(需 PSK)
密码套件数量 37 个(RFC 标准) 5 个
前向安全性 可选(RSA 密钥交换不支持) 强制(仅 (EC)DHE)
ChangeCipherSpec 显式发送 移除(兼容性空消息)
压缩 支持(有安全风险) 移除
重新协商 支持 不支持
会话恢复 Session ID / Session Ticket PSK(预共享密钥)

嗯,这里有个细节:TLS 1.3 的会话恢复机制完全基于 PSK,不再使用 Session ID 或 Session Ticket。PSK 可以是从之前握手导出的,也可以是外部预置的(比如在物联网设备中)。

4.5 知识体系总览

下面这张图把 TLS 1.3 的核心变化串起来了,你可以对照着理解:

TLS 1.3 新特性知识体系 TLS 1.3 新特性知识体系 TLS 1.3 核心变化 握手流程:1-RTT 0-RTT 快速重连 密码套件:仅5个 KeyShare 提前 移除 ChangeCipherSpec PSK 预共享密钥 重放攻击风险 强制 PFS 仅 AEAD 加密 核心变化总结 • 握手从 2-RTT 降到 1-RTT,0-RTT 实现真正的零往返重连 • 密码套件从 37 个精简到 5 个,强制前向安全性和 AEAD 加密 • 移除不安全特性(压缩、重新协商、静态 RSA 密钥交换)

4.6 Wireshark 实战:如何识别 TLS 1.3 握手

在 Wireshark 里抓包时,识别 TLS 1.3 有几个关键特征:

  • ClientHello 中的 supported_versions 扩展:TLS 1.3 的 ClientHello 会包含一个 supported_versions 扩展,里面列出 0x0304(TLS 1.3)。
  • 没有 ChangeCipherSpec:TLS 1.3 握手中不会出现 ChangeCipherSpec 消息(除非是兼容性空消息)。
  • ServerHello 后直接是 EncryptedExtensions:TLS 1.3 的 ServerHello 之后没有 Certificate 明文,而是先发 EncryptedExtensions,再发 Certificate(已加密)。
  • Finished 消息加密:TLS 1.3 的 Finished 消息是加密的,而 TLS 1.2 的 Finished 是明文。

过滤技巧:在 Wireshark 里用 tls.handshake.type == 1 and tls.handshake.ext_supported_versions 可以快速定位 TLS 1.3 的 ClientHello。如果看到 tls.handshake.type == 20(Finished)且是加密的,基本可以确认是 TLS 1.3。

我个人习惯在排查慢连接时,先看握手轮次。如果发现是 2-RTT,再检查是不是 TLS 1.2 或者 TLS 1.3 的 PSK 没命中。很多时候,问题就出在客户端没有缓存 PSK,导致每次都要完整握手。