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-RTT 握手,双方协商出一个 PSK(预共享密钥)。
- 后续连接:客户端在 ClientHello 里带上 PSK 标识和 0-RTT 数据(已经用 PSK 加密过)。
- 服务器收到后:如果能验证 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 的核心变化串起来了,你可以对照着理解:
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,导致每次都要完整握手。