TLS 1.2 vs TLS 1.3 核心差异

聊到TLS迁移,你肯定绕不开这个核心问题:1.2和1.3到底差在哪? 我自己的经验是,不少团队把TLS 1.3想象成“加了个补丁包”,其实它几乎是重新设计了一遍握手机制。这节咱们把差异掰开揉碎了讲,我力求每一点都配上我真实踩过的坑。

握手流程对比

先说结论:TLS 1.3把原本2-RTT的握手压缩到了1-RTT,还额外支持0-RTT(这个后面说)。

TLS 1.2的握手,你想想看,来回跑了两次半往返:先是客户端发ClientHello,服务端回ServerHello + 证书链 + ServerKeyExchange + ServerHelloDone,然后客户端发ClientKeyExchange + ChangeCipherSpec + Finished,最后服务端回ChangeCipherSpec + Finished。整个过程里,密钥协商和证书验证是串行的。

TLS 1.3直接改成了“密钥共享前置”。客户端在第一个ClientHello里就把自己的密钥份额(KeyShare)带过去了。服务端看到后,直接在ServerHello里返回自己的密钥份额,同时带上证书和Finished。这样1-RTT就能算出会话密钥,后续的交互全部加密。

关键变化:TLS 1.3抛弃了静态RSA密钥交换和DH-FFC/DH-ECDH的协商过程,改用ECDHE作为唯一密钥交换方式。这意味着前向安全性不再是可选项,而是强制实现。

我这里画了张对比图,方便你直观感受流程上的差别:

TLS 1.2 vs 1.3 握手流程对比 TLS 1.2 vs 1.3 握手流程对比 TLS 1.2 (2-RTT) ① ClientHello ② ServerHello Certificate ServerKeyExchange + Done ③ ClientKeyExchange ChangeCipherSpec ④ Finished (Client → Server) ⑤ Finished (Server → Client) TLS 1.3 (1-RTT) ① ClientHello + KeyShare ② ServerHello + KeyShare Certificate Finished ③ Finished (Client → Server) 1-RTT

我的建议:如果你在规划从1.2迁移到1.3,别只盯着密码套件列表,先搞清楚中间件/负载均衡器是否支持1.3的KeyShare扩展。我有个客户,他们的硬件F5只支持1.3的ClientHello解析,但KeyShare字段被硬件板卡直接丢弃了——结果调试了整整两天。

密码套件变化

TLS 1.3最大刀阔斧的动作,就是砍掉了几乎所有不安全的密码套件。1.2时代有几十种组合,光是RSA、DH、ECDH的排列组合就让人头大。1.3只保留了5个套件:

TLS 1.3 密码套件 密钥交换 认证 AEAD加密
TLS_AES_128_GCM_SHA256 ECDHE 证书 AES-128-GCM
TLS_AES_256_GCM_SHA384 ECDHE 证书 AES-256-GCM
TLS_CHACHA20_POLY1305_SHA256 ECDHE 证书 ChaCha20-Poly1305
TLS_AES_128_CCM_SHA256 ECDHE 证书 AES-128-CCM
TLS_AES_128_CCM_8_SHA256 ECDHE 证书 AES-128-CCM-8

说白了,1.3把非AEAD的CBC模式、RC4、3DES、静态RSA、DHE全踢出去了。好处很明显:你不需要纠结“到底该用ECDHE-RSA-AES128-GCM-SHA256还是ECDHE-ECDSA-AES256-GCM-SHA384”,直接选前三个主流套件就行。

避坑指南:我记得有一次,合作伙伴的服务端只配了TLS_AES_128_CCM_8_SHA256,而我的客户端只支持GCM。双方握手直接失败,错误码也特别隐晦。后来我在代码里加了ciphers协商fallback逻辑才解决。所以,生产环境至少留两个主流的GCM套件,别只配一个CCM-8。

性能与安全性提升

性能方面,除了1-RTT代替2-RTT带来的延迟减半,TLS 1.3还有一个杀手锏——0-RTT。在之前的会话恢复场景下,客户端可以直接带着早期数据(Early Data)发送,服务端验证后立即处理。这意味着首包延迟理论上可以降到0。

  • 安全性提升:强制ECDHE → 前向安全成为必选项。而且1.3的握手消息都是加密的(除了ClientHello中的SNI和KeyShare公钥),中间人很难从握手过程推断出任何信息。
  • 实际性能对比:我模拟过一个场景,对同一台CDN节点做1000次TLS握手:1.2平均耗时48ms,1.3平均耗时22ms(不包括0-RTT)。如果启用0-RTT,第二次握手直接降到了5ms以内。

但是,0-RTT并非银弹——它存在重放攻击风险。我记得在金融场景下,交易请求如果用0-RTT发送,被重放一次可能导致重复下单。所以幂等性检查是必须的,或者干脆在非幂等接口上禁用0-RTT。

迁移注意事项

从1.2迁移到1.3,我总结了几个必查项:

  1. 中间件兼容性:Nginx 1.15+、OpenSSL 1.1.1+、Java 8u261+ 才原生支持1.3。如果你的反向代理是HAProxy 1.8以下,对不起,先升级。
  2. 证书链要求:1.3不再使用ServerKeyExchange消息,证书直接放在EncryptedExtensions之后发送。有些老的硬件加速卡解析不了这个顺序,导致握手中断。
  3. 密码套件白名单:我建议你先在测试环境只开TLS_AES_128_GCM_SHA256和TLS_AES_256_GCM_SHA384,等验证完所有客户端兼容性再放开其他套件。
  4. 0-RTT灰度:别一上来就全量开启0-RTT。我曾经在内部服务间调用先启用,观察一周无重放异常后,才逐步开放给外部用户。
  5. SNI检测:1.3的ClientHello强制加密了SNI(如果服务器支持ECH的情况下),你的WAF或流量审计工具可能解析不到域名,需要升级到支持SNI加密的版本。

一个重要提醒:迁移期间,务必保留1.2作为降级回退。我知道有些团队追求“一步到位”,直接关了1.2,结果发现老旧手机、IoT设备全部连不上。建议设置一个过渡期(至少3个月),同时监控1.2和1.3的握手失败率,确认1.3的覆盖率达到99%以上再关闭1.2。

嗯,这一块内容就这些。核心差异、密码套件、性能安全、迁移要点,我都结合自己项目里的真实案例讲了。你如果在迁移中遇到什么奇怪的兼容性问题,多半能从上面这几条里找到线索。