2. TLS 握手过程详解:ClientHello、ServerHello、证书交换、密钥交换、Finished 消息

好,咱们直接进入正题。上一章我们聊了为什么 HTTPS 需要 TLS 握手,这一章我就带你亲手拆开这个握手过程。你打开 Wireshark,抓一个 HTTPS 的包,看到的第一个动作就是 ClientHello。别急,我们一个一个来。

2.1 ClientHello —— 客户端发起“打招呼”

说白了,这就是客户端在说:“嘿,服务器,我想跟你建立安全连接,我支持这些加密方式,你看哪个合适?”

在 Wireshark 里,你双击一个 ClientHello 包,会看到这些关键字段:

  • Version:客户端能支持的最高 TLS 版本。比如 TLS 1.2 或 1.3。
  • Random:一个 32 字节的随机数。这个很重要,后面生成会话密钥全靠它。
  • Session ID:如果之前连过这个服务器,可以带上之前的 Session ID 做会话复用。
  • Cipher Suites:客户端支持的加密套件列表。我见过有些客户端列了十几个,其实真正用的就前面几个。
  • Extensions:扩展字段,比如 SNI(Server Name Indication),告诉服务器你想访问哪个域名。
我的经验: 有一次排查一个 HTTPS 连接失败的问题,发现客户端只支持 TLS 1.0,而服务器已经关闭了 1.0。Wireshark 里一看 ClientHello 的 Version 字段,问题一目了然。

2.2 ServerHello —— 服务器回应

服务器收到 ClientHello 后,会回复一个 ServerHello。它从客户端提供的列表中选一个双方都支持的加密套件,然后告诉客户端:“我们就用这个吧。”

ServerHello 里同样包含一个 Random 字段(也是 32 字节),以及服务器选中的 Cipher Suite。注意,这里的 Random 和客户端的 Random 不一样,它们是独立的。

嗯,这里要注意:如果服务器找不到双方都支持的加密套件,它会直接发一个 Alert 消息,握手就断了。我在项目中遇到过这种情况,原因是客户端太老,只支持 RC4,而服务器早就禁用了它。

2.3 证书交换 —— 服务器亮出“身份证”

ServerHello 之后,服务器会发送自己的 Certificate 消息。这个证书里包含了服务器的公钥、域名、颁发者等信息。Wireshark 里展开 Certificate 字段,你能看到证书链:

  • 服务器证书(叶子证书)
  • 中间 CA 证书
  • 根 CA 证书(通常不发送,客户端本地有)

你想想看,如果证书的域名和实际访问的域名不匹配,浏览器会直接报安全警告。Wireshark 里你可以直接看 Certificate 字段里的 Subject 和 Issuer,确认证书是谁发的、发给谁的。

避坑指南: 我曾经遇到一个案例,服务器发送的证书链不完整,缺少中间 CA 证书。客户端(比如 Android 手机)无法验证证书有效性,直接拒绝连接。Wireshark 里一看,Certificate 消息里只有一张叶子证书,中间 CA 没发过来。解决办法是让运维把完整的证书链配置上去。

2.4 密钥交换 —— 协商会话密钥

证书交换之后,客户端需要生成一个 Pre-Master Secret(预主密钥),然后用服务器的公钥加密它,发送给服务器。这个过程叫做 Client Key Exchange。

服务器收到后用私钥解密,拿到 Pre-Master Secret。然后双方用三个东西:

  • Client Random
  • Server Random
  • Pre-Master Secret

通过伪随机函数(PRF)生成 Master Secret(主密钥)。再进一步生成会话密钥,用于后续的对称加密。

在 Wireshark 里,如果你有服务器的私钥(RSA 密钥交换场景),可以配置 SSL 解密,Wireshark 会自动解密后续的加密数据。我个人习惯在实验室环境里用这种方式来验证握手过程是否正确。

密钥材料 长度 作用
Client Random 32 字节 防止重放攻击
Server Random 32 字节 防止重放攻击
Pre-Master Secret 48 字节 核心密钥材料
Master Secret 48 字节 派生会话密钥

2.5 Finished 消息 —— 握手完成确认

最后一步,双方各自发送 Finished 消息。这个消息的内容是对整个握手过程所有消息的哈希值(用 PRF 计算),并加上一个固定的标签 "client finished" 或 "server finished"。

如果双方都能正确验证对方的 Finished 消息,说明握手过程中没有人篡改数据,密钥协商也一致。之后就可以开始加密通信了。

在 Wireshark 里,Finished 消息的 Verify Data 字段就是那个哈希值。你可以自己算一下,看看是否匹配。我偶尔会这么干,用来验证 Wireshark 的解密是否正确。

核心要点: Finished 消息是握手的“最终确认”。如果这一步失败,说明握手过程中有人篡改了消息,或者密钥计算有误。我在排查一个跨地域的 HTTPS 连接问题时,发现 Finished 消息验证失败,最后定位到是中间防火墙修改了 ServerHello 里的 Random 字段。

2.6 完整握手流程图

下面这张图把整个握手过程串起来,你一看就明白了。

TLS 1.2 完整握手流程 TLS 1.2 完整握手流程 客户端 服务器 ① ClientHello ② ServerHello ③ Certificate ④ ServerHelloDone ⑤ ClientKeyExchange ⑥ ChangeCipherSpec + Finished 注意:实际握手中 ChangeCipherSpec 和 Finished 是两条独立消息,这里合并展示

你看,从 ClientHello 到 Finished,一共 6 步(不算 ChangeCipherSpec 的话是 5 步)。每一步都有明确的职责,Wireshark 里都能看到对应的包。我个人建议你抓一个真实的 HTTPS 流量,对照着这张图一个一个包看,印象会深很多。