TLS 1.2 握手流程 vs TLS 1.3 握手流程

做安全审计这些年,我见过最多的误判就是——拿着 TLS 1.2 的日志模板去套 1.3。结果呢?字段对不上,时序全乱,最后怀疑自己抓包工具坏了。其实说白了,这两个版本的握手逻辑差别挺大的。

我个人习惯在分析审计日志前,先搞清楚当前连接用的是哪个版本。因为同一个「ServerHello」字段,在 1.2 里它带着证书链和密钥交换参数,在 1.3 里它可能已经把 Finished 消息都塞进去了。你想想看,字段解析顺序都不一样,审计规则能一样吗?

TLS 1.2 握手——四次往返的「老江湖」

TLS 1.2 的握手,概括起来就是「你一次,我一次,再交换一次,最后确认一次」。一共需要 4 次网络往返(如果算上 TCP 三次握手就是 7 次)。

我在一个老旧银行系统里见过这种握手模式,每次建立连接都要拖到 200ms 以上。嗯,那还是纯内网环境。

  1. ClientHello:客户端发出支持的密码套件列表、随机数、扩展信息。
  2. ServerHello + Certificate + ServerHelloDone:服务端选好密码套件,发证书,结束自己这一轮。
  3. ClientKeyExchange + ChangeCipherSpec + Finished:客户端计算预主密钥,切换加密策略,发送验证消息。
  4. ChangeCipherSpec + Finished:服务端也切换加密策略,回复验证消息。
审计日志注意点:TLS 1.2 的 ChangeCipherSpec 是一条独立的记录,字段 code=0x14。很多审计工具会把这条和 Finished 混淆,我建议你在正则里单独标注。

TLS 1.3 握手——一次交互的秘密

TLS 1.3 通过「预共享密钥(PSK)模式」和「1-RTT 握手」把往返次数降到 1 次。首次连接也只需要 2 次交互就能完成握手。说白了,服务端在收到 ClientHello 后,直接就能把证书、密钥交换参数和 Finished 打包发回去。

记得有一次做性能调优,把内部 RPC 从 1.2 升级到 1.3,延迟直接砍半。我当时还纳闷,到底少了什么环节?后来看抓包才明白——握手流程简化太多了。

  1. ClientHello:客户端不仅发密码套件列表,还带上 key_share 扩展(包含自己的临时公钥)。
  2. ServerHello + EncryptedExtensions + Certificate + CertificateVerify + Finished:服务端一步到位,把选套件、发证书、验证、结束全放在一个包里。
  3. (可选 0-RTT)如果复用会话,客户端可以直接发加密的应用数据。
踩坑警告:我曾经在审计日志里发现 TLS 1.3 的 Finished 消息没有独立的 ChangeCipherSpec 前置标志。如果审计规则还按 1.2 的老逻辑去匹配 “14 00 00 00” 这种字节序,会直接丢包。一定要先检查 version 字段。

核心差异速览表

对比维度 TLS 1.2 TLS 1.3
握手往返次数 4 次(2-RTT) 2 次(1-RTT),0-RTT 可选
密钥交换机制 支持静态 RSA、DHE、ECDHE(协商方式) 仅支持 DHE 或 ECDHE,强制前向安全
密码套件选择 客户端发送完整列表,服务端挑选 客户端只发送支持的 DH 组和 AEAD 算法,服务端从剩余组合中选
ChangeCipherSpec 独立记录,code=0x14 不再作为独立记录,仅用于兼容性空包
证书与验证 ServerHello 后单独发送 Certificate 和 ServerKeyExchange Certificate 和 CertificateVerify 与 ServerHello 合并在一个飞行中
会话恢复 Session ID / Session Ticket PSK + ticket / 0-RTT
审计日志字段冗余度 高(独立记录多,字段解析简单但冗长) 低(合并记录多,需要根据 handshake_type 二次拆分)

流程对比图(SVG 内联)

下面这张图把两个版本握手的核心步骤画在一起。左侧是 TLS 1.2 的四次交互,右侧是 TLS 1.3 的两次交互。箭头上的数字是网络往返次数。

TLS 1.2 与 TLS 1.3 握手流程对比 TLS 1.2 与 TLS 1.3 握手流程对比 TLS 1.2 四次握手 TLS 1.3 两次握手 ① ClientHello 1-RTT ② ServerHello + Certificate + ServerHelloDone 2-RTT ③ ClientKeyExchange + ChangeCipherSpec + Finished 3-RTT ④ ChangeCipherSpec + Finished 应用数据开始 4-RTT ① ClientHello (含 key_share 扩展) 1-RTT ② ServerHello + EncryptedExtensions + Certificate + CertificateVerify + Finished 应用数据开始 全程 2-RTT + 4 次消息交互 全程 1-RTT + 2 次消息交互 ChangeCipherSpec 独立记录,共4条 无独立 CCS,共2条记录

审计日志里怎么区分这两个版本?

我直接给个实用技巧:看 Handshake Protocol: Version 字段。TLS 1.2 对应 0x0303,TLS 1.3 对应 0x0304。但要注意,有些中间设备会把版本号改写,我建议你联动 supported_versions 扩展来双重确认。

  • TLS 1.2 日志特征:出现独立的 ChangeCipherSpec 记录,ServerHello 之后紧接着 Certificate 和 ServerHelloDone,Finished 消息在 ChangeCipherSpec 之后。
  • TLS 1.3 日志特征:没有独立 CCS,ServerHello 之后直接加密扩展和证书验证,Finished 作为握手最后一包合并发送。
避坑要点:如果你在审计日志里看到 type 22 (Handshake) 后面跟着 type 20 (ChangeCipherSpec),那基本可以确定是 TLS 1.2 或更老的版本。TLS 1.3 中的 ChangeCipherSpec 只作为兼容性空包出现,不承载握手信息。

还是那句话,理解握手流程的差异,是写好审计规则的前提。我个人会把 TLS 1.2 的审计日志拆成 4 个阶段分别校验,而 TLS 1.3 只需要校验 ClientHello 和 ServerHello 两个关键点,剩下的字段合并检查就行。

嗯,这就是我处理这两个版本握手时的核心思路——先分版本,再定规则,最后看细节。这样审计日志才不会漏报或误报。