2. TLS 1.2 与 TLS 1.3 主要区别
讲抓包分析之前,我习惯先把两个版本的核心差异讲清楚。为什么?因为 Wireshark 里看到的报文结构完全不同。你要是不了解这些,抓了一堆包也看不懂。
TLS 1.3 是 2018 年发布的,距离 TLS 1.2(2008 年)整整十年。这十年里攻击手法变了太多,所以 1.3 相当于把 1.2 的握手流程给重写了。说白了,更快、更安全、也更难被中间人偷听。
2.1 握手流程:少了整整一个 RTT
我个人最喜欢拿「握手次数」来对比。1.2 需要 2 个 RTT 才能完成握手,1.3 只要 1 个。有些场景下甚至 0-RTT。
你看一下这张流程图就清楚了:
看到没?1.2 的流程里,客户端收到 ServerHello 之后还得再发一次密钥交换报文。1.3 直接把这一步塞进第一次 ClientHello 里了。我在项目里帮客户看一个视频直播延迟问题时,发现所有 RTT 加起来超过 100ms,换成 1.3 后直接降了一小半。
2.2 密码套件:从几十种砍到几种
1.2 支持的密码套件多得让人头疼。我记得刚入行时拿 Wireshark 看 ClientHello 里的 cipher suites 列表,长长一串吓死人。
为什么这样设计?因为当年厂商各自为政,都想塞进自己支持的算法。但实际上很多套件已经不安全了,比如 RC4、CBC 模式的 3DES。1.3 直接动了刀子,只保留了 AEAD 类算法。
| 版本 | 支持的密码套件数量 | 典型套件举例 | 备注 |
|---|---|---|---|
| TLS 1.2 | 30+(客户端可发送) | TLS_RSA_WITH_AES_128_CBC_SHA | 含 CBC 模式、RC4、DES 等老旧算法 |
| TLS 1.3 | 5 个(仅 5 个) | TLS_AES_128_GCM_SHA256 | 只支持 AEAD + HKDF 派生 |
抓包时的直观区别:
- 1.2 的 ClientHello 里 cipher_suites 字段很长,常有 20~30 个
- 1.3 的 ClientHello 里只看到 5 个,而且以
TLS_AES_*开头 - 1.3 不再有
TLS_RSA_*系列 —— RSA 密钥交换被彻底移除
你想想看,套件少了意味着什么?协商更快,而且没有「降级攻击」的风险。1.2 时代常见的攻击就是强迫客户端和服务端选一个弱套件,比如把 AES 换成 RC4。1.3 直接不让选,就那几样,穷举都不够瞧的。
2.3 密钥交换:RSA 再见,DH 全家桶上线
1.2 常用的密钥交换有两种:RSA 和 DHE(ECDHE)。RSA 密钥交换有个严重问题——服务端私钥一旦泄露,之前所有会话都能被解密。这就是所谓的「前向安全性」缺失。
我在一次安全审计时遇到过一个案例:客户用 RSA 密钥交换,三年前的私钥备份被泄露,结果整个历史通信都被翻了个底朝天。从那以后我所有项目都强制要求只用 ECDHE。
1.3 更绝,直接把 RSA 密钥交换删干净了。只能用 (EC)DHE。每次握手都会生成临时密钥对,握手结束就丢弃。即使以后私钥泄露,之前的历史数据依然安全。
另外,1.3 把密钥派生算法统一成了 HKDF(基于 HMAC 的密钥派生函数),不再像 1.2 那样每个套件各自实现一套 PRF。这个改动让实现更简单,也减少了由于实现差异导致的漏洞。
2.4 证书与签名:只保留数字签名,砍掉证书传输的冗余
1.3 证书验证流程其实没变,还是 X.509 那套。但一个变化是:1.3 不再支持「服务器密钥交换」中携带签名(ServerKeyExchange 消息被移除),证书的签名只在 Certificate 消息中携带。
还有,1.3 支持 Certificate Authorities Extension,客户端可以在 ClientHello 里告诉服务端「我只信任这几家 CA」。这在企业内网很有意思——有一次我调试一个银行内网应用,客户端死活连不上,后来一看是客户端只认内网 CA,而服务端发来了公网证书,1.3 的这个扩展可以提前终止握手,避免了 1.2 时代那种「收到证书才发现不信任」的浪费。
2.5 变更密码规范(CCS)消息的变化
1.2 里有专门的 Change Cipher Spec 消息,表示「接下来要换加密参数了」。这条消息本身不加密,是明文发送的。1.3 其实还保留了这个消息,但它的作用变了——只是为了兼容中间件。实际上 1.3 的握手过程中,加密参数是在 ServerHello 之后就生效了,CCS 只是一个形式。
你抓包时会发现,1.3 的 CCS 消息出现的时机和 1.2 不同。1.2 是在 Finished 之前,1.3 可能出现在任何位置,甚至客户端可以完全不发。我建议你在分析时不要过度关注 CCS,重点看 EncryptedExtensions 和 Finished 的先后关系。
2.6 会话恢复与 0-RTT
1.2 的会话恢复靠 Session ID 或 Session Ticket。客户端需要重新发 ClientHello 带上 ticket,服务端验证后恢复会话,仍然需要 1 个 RTT。
1.3 引入的 PSK(预共享密钥)可以做到 0-RTT。如果你之前已经和服务器建立过连接,第二次握手时客户端可以直接在 ClientHello 里携带加密数据,服务端收到就能直接解密。这个在移动端非常实用,减少频繁握手的延迟。
early_data 扩展。如果看到 Extended Master Secret 标记为 Not Used,说明启用了 0-RTT。
2.7 抓包实际识别要点
最后给你一个快速判断表。在 Wireshark 里只要看几个关键字段:
| 特征 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Handshake 消息数量 | 通常 4~5 条(不含 CCS) | 通常 2~3 条(不含 CCS) |
| ServerHello 后的下一条消息 | Certificate(明文) | EncryptedExtensions(加密后就加密了) |
| Certificate 是否加密 | 明文传输 | 加密传输(被 EncryptedExtensions 保护) |
| Finished 消息长度 | 12 字节(verify_data) | 32 字节(基于 SHA256) |
| CCS 消息存在 | 必须存在,紧挨着 Finished | 可存在但无实际意义 |
嗯,我记得有一次线上排查一个慢连接问题,看 Wireshark 里 ServerHello 之后直接是 EncryptedExtensions,就秒确认是 1.3。然后发现是客户端和服务端协商了 0-RTT 但 early_data 没通过校验导致重发,排查起来快多了。如果你还在用 1.2 的习惯去抓 1.3 的包,很容易被绕晕。