3. 完整的TLS握手过程详解

说实话,干了这么多年安全优化,最能看出团队基本功的,就是看他们对TLS握手理解得透不透。我面试时经常问这个问题——不是要你背RFC,而是看你能不能把这条“加密通道”的搭建逻辑说清楚。

好,咱们直接进入正题。一个完整的TLS 1.2握手,总共11步,分四个阶段。你别怕数字,理解了本质,你会发现这就像两个人交换保险柜钥匙,只不过用的是数学。

3.1 握手究竟在干什么?

说白了,就是三件事:

  • 身份验证:确认服务器(有时客户端)是它声称的那个。
  • 密钥协商:双方面对面定一个只有你们知道的临时密码。
  • 算法确认:统一用哪种加密、哪种哈希。

我习惯把TLS握手比作“面对面喊话”和“悄悄传纸条”的组合。前半段喊话是明文,后半段传纸条就开始加密了。

3.2 TLS 1.2 全握手流程

下面这张图是我自己画的标准流程——每个箭头背后都有一组报文交互。

图解重点:注意图中客户端和服务器两侧的“Change Cipher Spec”报文。很多刚入门的同学以为它属于握手协议,其实它是个独立的协议,专门用来“切换密钥状态”。

TLS 1.2 完整握手流程(11步) 客户端 (Client) 服务器 (Server) ClientHello 密钥套件列表 + 随机数 1 ServerHello + Certificate + ServerKeyExchange + ServerHelloDone 2 ClientKeyExchange 携带预主密钥(加密) 3 ChangeCipherSpec 切换至加密状态 Finished 验证握手完整性 ChangeCipherSpec + Finished 6 ✅ 加密应用数据传输 (对称加密) 握手完成 ★ 第4步和第6步的 ChangeCipherSpec 不属于握手协议

3.3 流程拆解——每一步都在干什么?

我再给你用文字过一遍,每个阶段都有哪些报文,你对着图看。

阶段一:Hello 协商(第1步)

客户端发 ClientHello,里面包含:

  • 最高支持TLS版本(比如1.2)
  • 一个32字节的随机数(Client Random)
  • 支持的加密套件列表(Cipher Suites)
  • Session ID(用于会话复用,以后会细讲)

服务器收到后,回复 ServerHello,带上:

  • 选定的TLS版本
  • 自己的随机数(Server Random)
  • 选定的加密套件
  • 还会携带自己的证书链(Certificate)、DH参数(ServerKeyExchange)、最后说一句“我说完了”(ServerHelloDone)。

这里要注意,如果服务器用的是RSA密钥交换,就不会有ServerKeyExchange报文。嗯,这块很多人会搞混。

阶段二:密钥交换(第3步)

客户端验证证书后,取出公钥,生成一个48字节的预主密钥(Pre-Master Secret),用服务器公钥加密后通过 ClientKeyExchange 发送出去。

这时候,双方都能算出主密钥(Master Secret)了。算法是:

Master Secret = PRF(Pre-Master Secret, "master secret", Client Random + Server Random)

PRF是伪随机函数,在TLS 1.2里用的是SHA-256。

阶段三:切换密码状态(第4、5、6步)

客户端先发 ChangeCipherSpec,通知服务器“我接下来要加密通信了”。然后立刻发 Finished——这是第一个加密的报文,里面包含对之前所有握手消息的MAC校验。

服务器同样回复 ChangeCipherSpec + Finished。直到双方都收到并验证对方的Finished消息,握手才算真正完成。

我曾经在线上排查过一次问题:客户端发完Finished后等不到服务器的Finished,查了半天发现服务器端证书链验证超时(有一张中间CA证书没配置)。这种坑,你得做到心中有数。

3.4 TLS 1.3 有什么不同?

你也许好奇,为什么不直接从1.3开始讲?因为现实中80%以上的服务还跑在1.2上(2025年的数据)。但了解1.3能帮你理解为什么1.2需要优化。

对比项 TLS 1.2 TLS 1.3
握手往返次数 2 RTT(完整握手) 1 RTT(完整握手)
密钥交换方式 RSA / DH (可协商) 仅 ECDHE(强制前向安全)
ServerHello后发送 Certificate 单独发送 Certificate 加密后发送(更隐私)
支持的加密套件 几十种 5种(更简单更安全)

1.3里把密钥交换和证书验证压缩到一个往返里,而且去掉了RSA这种不支持完美前向安全的算法。说实话,我建议新业务直接上1.3,但如果你要兼容旧设备,1.2还得扛着。

3.5 容易被忽略的两个细节

Tip:证书链验证 — 客户端在收到ServerCertificate后,必须验证证书的有效性(过期、吊销、CA信任链)。这一步如果跳过,中间人攻击就能轻易得手。我见过有人为了省事,在生产环境关闭了证书验证,后果可想而知。

Warning:ChangeCipherSpec 的位置 — 记住,它不属于握手协议(Handshake Protocol),而是 Alert 级别的 ChangeCipherSpec Protocol。发送时不能附带额外数据。有的老旧实现会把它和Finished合并在一个TCP包,导致接收方解析出错。

另外,握手阶段的所有消息都是连续发送的,但接收方必须按序处理。有一次我调试时发现,客户端把ClientHello和ClientKeyExchange塞在同一个包发过去,服务端解析ClientKeyExchange时还没拿到客户端的随机数,直接报错。原因就是没有等ServerHello先到达。所以,别看就是发几个包,顺序乱一次,连接就断了。

3.6 握手开销——你优化的是什么?

完整TLS 1.2握手需要2个网络往返(2-RTT)。如果加上TCP三次握手,总耗时至少3-RTT。以100ms的RTT为例,光握手就要300ms。所以后面要讲的会话复用、OCSP Stapling,核心都是砍掉部分握手步骤,减少网络延迟。

记住这个数字:一次完整TLS 1.2握手,客户端和服务器各做了:1次证书验证、2次公私钥运算、3次HMAC计算。单次连接的开销几乎都花在非对称加密上(RSA或ECDHE)。