2、TLS 1.2与TLS 1.3核心差异:对比两个版本的握手流程、安全性及性能改进
先聊个我自己的体验。几年前我调一个跨平台支付SDK,Android端连服务器总是慢半拍,尤其在弱网环境。抓包一看,TLS握手就占了将近两个往返。后来换成TLS 1.3,整个世界清静了。说白了,这两个版本虽然名字只差一个小数点,但底层的设计哲学完全是两代人。
2.1 握手流程:从“四步一卡”到“一步到位”
TLS 1.2的完整握手,你想想看,客户端和服务器要来回扯好几次皮:
- ClientHello:客户端报上支持的密码套件、随机数。
- ServerHello + Certificate + ServerHelloDone:服务器选套餐,亮证书,说“我讲完了”。
- ClientKeyExchange + ChangeCipherSpec + Finished:客户端生成预主密钥,通知“我要加密了”,再发一个校验消息。
- ChangeCipherSpec + Finished:服务器同样切到加密,完成握手。
嗯,这里要注意,两个完整的网络往返(2-RTT)才建立起安全通道。遇到丢包或高延迟,这个代价可不小。我记得有一个项目,用户在日本连新加坡的服务器,光握手就花了200多毫秒,用户体验直呼受不了。
再来看TLS 1.3,它把握手压缩到了1-RTT,甚至0-RTT(带早发数据)。核心做法是:客户端在ClientHello里直接带上密钥协商的“半成品”(Key Share),服务器收到后能马上算出会话密钥,并立即发送ServerHello和证书,同时就开始传加密数据了。没有单独的ChangeCipherSpec,也没有那堆冗余的握手消息。
我用表格罗列一下主要区别,这样更直观:
| 对比项 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 完整握手RTT | 2-RTT | 1-RTT |
| 恢复握手RTT | 1-RTT(Session ID/Ticket) | 0-RTT(PSK + 早发数据) |
| 主要握手消息 | ClientHello, ServerHello, Certificate, ServerKeyExchange, ClientKeyExchange, ChangeCipherSpec ×2, Finished ×2 |
ClientHello, ServerHello, EncryptedExtensions, Certificate, CertificateVerify, Finished ×2 |
| ChangeCipherSpec | 必须有,且占一个独立消息 | 禁止发送(除兼容性降级外) |
| 密钥协商方式 | 支持RSA、DH、ECDH | 强制使用(EC)DHE,抛弃RSA密钥传输 |
看到没?TLS 1.3砍掉了ChangeCipherSpec这个历史遗留物,也禁止了静态RSA密钥传输。为什么这么狠?咱们下面说安全性的问题。
核心结论:TLS 1.3的低延迟特性,在弱网、移动端、物联网等跨平台场景下优势非常明显。我建议新项目直接上1.3,老项目逐步兼容。
2.2 安全性:不是小修小补,而是重构
说实话,TLS 1.2的安全性本身不差——只要你不作死用RC4、CBC模式的3DES或者静态RSA密钥交换。但是问题就在于,“不要作死”这个要求在实际落地中很难保证。我见过太多配置:
- 服务器同时支持RC4和AES-GCM,结果协商到RC4。
- 证书里用RSA 2048,但密钥交换也用了RSA,没有前向安全性。
- 允许降级到TLS 1.1甚至1.0,被POODLE攻击一打一个准。
TLS 1.3干脆把这些坑全部填上了:
- 强制前向安全性:只允许(EC)DHE密钥协商,服务器私钥泄露不能解密历史通信。
- 废弃不安全算法:RC4、CBC模式、3DES、静态RSA一个不留。只剩下AEAD(AES-GCM、ChaCha20-Poly1305)。
- 减少协商自由度:密码套件从几十个压缩到5个。服务器没有机会选错。
- 握手消息加密:ServerHello之后的握手消息全部加密。证书、服务器参数都在加密隧道里传递,中间人看不到。
- 移除压缩、重协商:CRIME攻击、重协商注入攻击的土壤被彻底铲除。
我这里有一个实际案例:
我曾经排查过一个跨平台客户端,iOS端走TLS 1.2正常,但Android老旧设备总是连接失败。捕获TLS握手发现,Android设备强制要求使用DHE-RSA-AES256-SHA,但服务器只开放了ECDHE-RSA-AES128-GCM-SHA256。在TLS 1.2里,这种套件不匹配就直接挂了。换成TLS 1.3之后,两边都支持TLS_AES_128_GCM_SHA256,问题迎刃而解。
2.3 性能改进:不止是少一个RTT
很多人以为TLS 1.3只是把2-RTT变成1-RTT。其实还有几个更重要的优化:
- 0-RTT(早发数据):对于复访用户,客户端可以在ClientHello里直接发送HTTP请求。这在API调用、会话续传场景下是质的飞跃。当然,有重放攻击风险,适合幂等请求。
- 更快的证书链处理:TLS 1.3不再要求服务器发送完整的证书链到根,可以只发终端证书,或者使用OCSP Stapling的优化版本。当然,实际中为了兼容,大部分服务器还是会发到根。
- 服务器端性能提升:只有5个密码套件需要计算,减少算法协商和内存分配的开销。对于高并发服务器,每个握手的CPU开销能降30%左右。
- 减少握手状态机复杂度:TLS 1.2的状态机有多个分支(会话恢复、重建、ECC vs RSA等),容易出bug。TLS 1.3是线性的,实现更简单,出Bug的概率更低。
注意:0-RTT有个坑——它不提供重放保护。这意味着同一个Early Data如果被攻击者截获并重放,服务器会误认为是合法请求。在跨平台实现中,只对幂等接口(比如GET、无副作用的POST)开放0-RTT。我习惯在应用层加上nonce或时间戳做二次校验。
2.4 一图看懂核心差异
下面我画了一张对比图,左侧是TLS 1.2的两轮握手,右侧是TLS 1.3的一轮握手。注意看中间的密钥协商和加密边界:
小结: TLS 1.3不是TLS 1.2的简单增量升级,它是一次彻底的重新设计。砍掉了历史包袱,牺牲了极端向后兼容,换来了更低延迟、更高安全性和更简洁的实现。作为跨平台开发者,我个人的建议是:如果你的客户端和服务器都能控制在相对现代的环境(Android 5+、iOS 9+、Windows 10+、OpenSSL 1.1.1+),直接切换到TLS 1.3。如果需要兼容古董设备,把TLS 1.2作为降级选项,但要严格限制密码套件范围。