TLS 握手流程简介
聊TLS握手之前,我先讲个真实的事。几年前我排查一个支付接口的故障,发现客户端和服务端互相不信任,日志里全是握手失败的记录。折腾了一上午,最后发现是TLS版本不匹配——客户端坚持用TLS 1.0,服务端只接受1.2以上。你说冤不冤?
所以,搞懂TLS握手的基本流程,是审计日志工作的第一步。说白了,握手就是通信双方在正式传数据之前,先互相打招呼、亮证件、商量加密规则的过程。
握手的核心目标
TLS握手要解决三个问题:
- 身份认证:确认对方是不是它声称的那个人
- 密钥协商:在不安全的信道上安全地商量出一个对称密钥
- 参数同步:双方确认用哪个TLS版本、哪套密码套件
你想想看,这三个目标任何一个出问题,后面的通信都没法谈。我在项目中见过不少因为证书过期导致握手失败的案例,看起来很基础,但真遇到了还是很头疼。
一次典型的TLS 1.2完整握手
我记得刚入行那会儿,老工程师跟我说:“看TLS握手,就看四个包。”
确实,TLS 1.2的完整握手,核心就四个消息交换:
- ClientHello:客户端发起握手,告诉服务端它能支持的TLS版本、密码套件列表,还会生成一个随机数
- ServerHello:服务端从客户端列表里挑一个版本和密码套件,也生成一个随机数返回
- Certificate:服务端把自己的证书发给客户端,证明身份
- ServerHelloDone:服务端说“我这边消息发完了”
客户端收到服务端的证书后,会做两件事:验证证书合法性,然后用服务端公钥加密一个预主密钥(Pre-Master Secret)发回去。
服务端收到后用私钥解密,这样双方都有客户端随机数、服务端随机数、预主密钥——三个东西一起算出主密钥。
关键点:审计日志里最值得关注的,就是ClientHello里带的密码套件列表和ServerHello里最终选定的密码套件。这两个参数直接决定了通信的加密强度。
TLS 1.3的优化
TLS 1.3出来后,握手的流程做了大改。我最喜欢它的一点——去掉了那些不必要的往返。
| 对比项 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手耗时 | 2个RTT(往返时间) | 1个RTT(首次),0-RTT(复用) |
| 证书 | 单独发送 | 加密后发送 |
| 密钥交换 | RSA或DH系列 | 仅支持ECDHE/DHE |
| ChangeCipherSpec | 单独消息 | 不再需要 |
你可能会问,为什么TLS 1.3这么快?说白了就是把密钥交换的参数提前放到了ClientHello里。服务端收到后直接算出主密钥,不用等客户端来回折腾。我测试过一个WebRTC场景,从TLS 1.2升级到1.3,握手延迟从80ms降到了40ms左右。
审计提示:1.3的握手参数记录更少,但更关键。因为密码套件只有固定的几个,所以审计时重点关注是否启用了0-RTT——0-RTT虽然快,但有重放攻击风险。
握手中的关键参数记录点
做审计日志时,我建议对每个握手阶段记录以下参数:
- ClientHello阶段:TLS版本(如0x0303表示TLS 1.2)、密码套件列表、扩展字段
- ServerHello阶段:实际选用的密码套件、服务器随机数
- 证书阶段:证书链长度、证书公钥算法(RSA、ECDSA等)、证书有效期
- 密钥交换阶段:使用的密钥交换算法、临时公钥的曲线参数
- 握手完成阶段:是否使用了会话复用、是否启用了安全重协商
踩坑提醒:我曾经排查过一个TLS握手失败,日志里只记录了“handshake failure”,没有任何细节。后来才发现是服务端要求客户端证书,但客户端没发。所以审计日志至少要记录“哪一步失败、原因是什么”,否则排查起来太痛苦。
一张图看懂TLS握手流程
下面这张图是我自己整理的,涵盖了TLS 1.2和1.3的典型握手流程。你看一眼就能明白整个骨架。
从审计视角看握手参数
我做审计日志方案时,特别在意下面这几个参数:
- 密码套件:是否包含弱算法(比如RC4、CBC模式、NULL加密)
- 证书链:是否为自签名证书、是否过期、是否被吊销
- 密钥交换算法:是否使用RSA密钥传输(RSA没有前向安全性)
- 扩展字段:比如SNI(Server Name Indication)是否缺失、ALPN协商结果
我曾经遇到过一个坑——某内部系统直接用TLS 1.0,密码套件是TLS_RSA_WITH_RC4_128_SHA。这在2018年就被认为不安全了。审计日志里如果你只记了“握手成功”,那这个隐患永远发现不了。所以,握手参数的完整性记录,比握手成功与否重要得多。
嗯,这一章的核心就是三句话:握手是双方协商加密参数的过程;TLS 1.2和1.3流程差别很大;审计日志的关键是记录协商结果,而不是只记成功失败。