4. TLS版本演进与性能差异

TLS 1.3(RFC 8446)并不是 TLS 1.2 的小幅修订,而是一次针对“延迟”和“安全性”同时重构的协议。理解两者性能差异的关键,不在于记住版本号,而在于看清握手轮次、密钥交换机制、0-RTT 与会话恢复这四条主线如何共同决定一次连接要花几个 RTT、消耗多少 CPU、能否复用。

握手轮次:从 2-RTT 到 1-RTT 的实质变化

在 TLS 1.2 的完整握手中,客户端先发 ClientHello,服务端回 ServerHello、Certificate、ServerKeyExchange、ServerHelloDone;客户端再发 ClientKeyExchange、ChangeCipherSpec、Finished;服务端最后回 ChangeCipherSpec、Finished。整个流程需要 2 个 RTT 才能开始传输应用数据。若启用客户端证书,还会再多一轮。

TLS 1.3 把密钥交换的“参数协商”提前合并进 ClientHello 的 key_share 扩展中。客户端在第一个飞行包(flight)里就带上自己猜测的密钥交换公钥,服务端若接受,直接在 ServerHello 中返回自己的 key_share,随后即可发送加密的 Certificate、Finished。因此完整握手压缩到 1-RTT。对于已经访问过的站点,客户端还可以利用 PSK 直接发送 early data,实现 0-RTT。

需要强调的是,1-RTT 是“完整握手”的典型值,不是所有场景的保证。如果服务端不接受客户端的 key_share 组(例如客户端只发了 X25519,而服务端只支持 P-256),服务端会发 HelloRetryRequest,额外增加 1 个 RTT。因此实际部署中,客户端和服务端都应同时支持 X25519 与 P-256,避免重试。

密钥交换:从 RSA 到 (EC)DHE 的全面前向安全

TLS 1.2 允许使用静态 RSA 密钥交换:客户端用服务端证书公钥加密预主密钥(premaster secret)后发送。这种方式没有前向安全性(PFS),一旦服务端私钥泄露,历史流量可被解密。TLS 1.2 也支持 ECDHE,但属于可选。

TLS 1.3 强制使用 (EC)DHE 或 PSK,彻底移除了静态 RSA 和自定义 DH 组。密钥交换的默认推荐组是 X25519,其次 P-256。X25519 在同等安全强度下计算更快、密钥更短,对移动端和高并发服务端都更友好。此外,TLS 1.3 的密钥派生使用 HKDF,握手密钥与应用密钥分离,配合“密钥更新(KeyUpdate)”机制,可以在长连接中定期更换应用流量密钥。

0-RTT:收益与风险并存

0-RTT 允许客户端在第一个飞行包中携带 early data,服务端用 PSK 派生密钥直接解密,从而在 0 个额外 RTT 内开始处理请求。这对 API 首包、静态资源、幂等查询类请求收益明显。

但 0-RTT 数据不具备前向安全性,且可被重放。因此工程上必须限制:只对幂等请求(GET、HEAD)开启;服务端应记录 anti-replay 窗口(通常基于 ticket 生命周期);对非幂等操作(POST、支付、下单)禁用 0-RTT。Nginx 中可通过 ssl_early_data on; 开启,并在应用层用 Early-Data: 1 请求头识别并做重放防护。

会话恢复:Session ID / Session Ticket 与 PSK

TLS 1.2 的会话恢复依赖 Session ID 或 Session Ticket。Session ID 需要服务端保存会话状态,多机部署时需共享存储;Session Ticket 把状态加密后交给客户端保存,服务端无状态,但 ticket 密钥轮换和加密套件绑定较复杂。

TLS 1.3 统一用 PSK 做恢复。服务端在完整握手后下发 NewSessionTicket,客户端下次连接时在 ClientHello 的 pre_shared_key 扩展中携带该 ticket。恢复握手只需 1 个 RTT(若配合 0-RTT 则 0-RTT)。PSK 与密钥交换可以组合,形成“PSK + (EC)DHE”模式,既恢复会话又保持前向安全。

TLS 1.2 与 TLS 1.3 握手轮次与恢复机制对比 TLS 1.2 完整握手(2-RTT) 客户端 服务端 ClientHello ServerHello + Certificate + ServerKeyExchange + ServerHelloDone ClientKeyExchange + CCS + Finished CCS + Finished 应用数据开始传输(2 RTT 后) 会话恢复(1-RTT) ClientHello + SessionTicket / SessionID ServerHello + CCS + Finished CCS + Finished 应用数据开始传输(1 RTT 后) 密钥交换:RSA(无 PFS)或 ECDHE(可选) 恢复机制:Session ID(有状态)/ Session Ticket(无状态) 0-RTT:不支持 TLS 1.3 完整握手(1-RTT) 客户端 服务端 ClientHello + key_share(X25519/P-256) ServerHello + key_share + {Certificate + Finished} {Finished} + 应用数据 应用数据开始传输(1 RTT 后) 0-RTT 恢复(0-RTT) ClientHello + pre_shared_key + early_data ServerHello + {Finished} + 应用数据 应用数据随首包发出(0 RTT) PSK 恢复(1-RTT) ClientHello + pre_shared_key + key_share ServerHello + {Finished} + 应用数据 密钥交换:强制 (EC)DHE 或 PSK,默认 X25519 恢复机制:NewSessionTicket → PSK(可组合 (EC)DHE)

迁移策略:从双栈并行到逐步收敛

生产环境迁移 TLS 1.3 不建议“一刀切”。推荐路径是:先让服务端同时支持 TLS 1.2 与 TLS 1.3,客户端优先协商 1.3;观察握手成功率、0-RTT 接受率、重放拦截日志;再逐步对内部服务、静态资源、幂等 API 开启 0-RTT;最后根据客户端兼容性数据决定是否关闭 1.2。

以 Nginx 为例,一份可参考的配置如下:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_ecdh_curve X25519:P-256;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets on;
ssl_early_data on;
add_header Early-Data $ssl_early_data;

其中 ssl_prefer_server_ciphers off 让客户端优先选择,符合 TLS 1.3 的设计;ssl_ecdh_curve X25519:P-256 同时提供两个组,降低 HelloRetryRequest 概率;ssl_early_data on 开启 0-RTT,但必须配合应用层重放防护。

性能对比与判断依据

维度 TLS 1.2 TLS 1.3
完整握手轮次 2-RTT 1-RTT(HelloRetryRequest 时 2-RTT)
会话恢复轮次 1-RTT 1-RTT(PSK)或 0-RTT(early data)
密钥交换 RSA(无 PFS)或 ECDHE(可选) 强制 (EC)DHE 或 PSK,默认 X25519
前向安全 仅 ECDHE 套件具备 默认具备
0-RTT 不支持 支持,但需防重放
典型首字节延迟 约 2×RTT + 计算 约 1×RTT + 计算(0-RTT 时约 0×RTT)

判断迁移是否成功,可以观察三个指标:一是 TLS 1.3 协商占比是否持续上升;二是完整握手 P95 延迟是否下降约一个 RTT;三是 0-RTT 请求中是否出现异常重放告警。若 HelloRetryRequest 比例偏高,应检查客户端与服务端的 key_share 组是否对齐;若 0-RTT 被大量拒绝,应检查 ticket 生命周期与 anti-replay 窗口是否过短。

TLS 1.3 的性能收益主要来自“少一个 RTT”和“更快的密钥交换”,但 0-RTT 的收益必须用重放防护来换。对非幂等接口,宁可放弃 0-RTT,也不要引入重放风险。
部分老旧客户端或中间设备可能不支持 TLS 1.3 的某些扩展,导致握手失败或降级。迁移前应采集真实客户端的 ClientHello 特征,确认 key_share 组、签名算法和 PSK 扩展的兼容性,再决定是否关闭 TLS 1.2。