4. TLS 1.2 握手流程详解:ClientHello、ServerHello、证书交换、密钥交换、Finished消息
TLS 1.2 握手协议是安全通信的核心,负责在客户端和服务器之间协商加密参数、验证身份并生成共享密钥。整个握手过程分为五个主要阶段,每个阶段都承载着特定的密码学功能。下面将逐一拆解这些消息的格式、作用与安全含义。
4.1 握手流程总览
TLS 1.2 完整握手(非会话恢复)通常需要 2 个网络往返(2-RTT)。下图展示了从 ClientHello 到 Finished 的完整消息流,以及各阶段使用的密码学组件。
4.2 ClientHello —— 客户端发起协商
握手的第一步由客户端发起。ClientHello 消息携带了客户端支持的密码学能力清单,供服务器选择。
| 字段 | 长度 | 说明 |
|---|---|---|
| Protocol Version | 2 字节 | 客户端支持的最高 TLS 版本,如 0x0303 表示 TLS 1.2 |
| Random | 32 字节 | 客户端生成的随机数(28 字节随机 + 4 字节时间戳),用于后续密钥派生 |
| Session ID | 可变 | 用于会话恢复;若为空表示新建会话 |
| Cipher Suites | 可变 | 客户端支持的密码套件列表,按优先级排序 |
| Compression Methods | 可变 | 支持的压缩算法列表(TLS 1.2 通常只包含 null) |
| Extensions | 可变 | 扩展字段,如 SNI、ALPN、supported_groups、signature_algorithms 等 |
4.3 ServerHello —— 服务器选择参数
服务器收到 ClientHello 后,从客户端提供的选项中选择一组参数,通过 ServerHello 返回。
| 字段 | 长度 | 说明 |
|---|---|---|
| Protocol Version | 2 字节 | 服务器选择的版本,通常等于客户端提供的最高版本 |
| Random | 32 字节 | 服务器生成的随机数,结构与客户端相同 |
| Session ID | 可变 | 若支持会话恢复,返回对应的 Session ID |
| Cipher Suite | 2 字节 | 服务器从客户端列表中选择的一个密码套件 |
| Compression Method | 1 字节 | 选择的压缩算法,通常为 null (0x00) |
| Extensions | 可变 | 服务器支持的扩展响应 |
ServerHello 完成后,双方已确定:协议版本、密码套件、压缩算法,以及两个随机数(client_random + server_random)。这些是后续密钥派生的基础材料。
4.4 证书交换 —— 服务器身份证明
服务器在 ServerHello 之后立即发送 Certificate 消息,将 X.509 证书链传递给客户端。这是 TLS 信任模型的基石。
- 证书内容: 包含服务器公钥、颁发者信息、有效期、签名算法、数字签名等。
- 证书链: 通常包含服务器证书 + 中间 CA 证书,根 CA 证书由客户端本地信任存储提供。
- 验证流程: 客户端逐级验证证书签名,检查有效期、吊销状态、域名匹配。
- 公钥用途: 证书中的公钥用于后续密钥交换(如 RSA 加密 Pre-Master Secret)或签名验证(如 DHE/ECDHE 中的签名)。
4.5 密钥交换 —— 协商共享秘密
密钥交换阶段是 TLS 1.2 握手中最核心的密码学环节。根据选择的密码套件,密钥交换方式分为两大类:
| 密钥交换算法 | 前向安全性 | 服务器消息 | 客户端消息 |
|---|---|---|---|
| RSA | 否 | Certificate(含 RSA 公钥) | ClientKeyExchange(RSA 加密的 Pre-Master Secret) |
| DHE_RSA | 是 | ServerKeyExchange(DH 参数 + 签名) | ClientKeyExchange(DH 公钥) |
| ECDHE_RSA | 是 | ServerKeyExchange(ECDH 参数 + 签名) | ClientKeyExchange(ECDH 公钥) |
| ECDHE_ECDSA | 是 | ServerKeyExchange(ECDH 参数 + 签名) | ClientKeyExchange(ECDH 公钥) |
Pre-Master Secret 的生成:
- RSA 模式: 客户端生成 48 字节的 Pre-Master Secret(2 字节版本 + 46 字节随机数),用服务器公钥加密后发送。
- DHE/ECDHE 模式: 双方各自生成临时密钥对,交换公钥后通过 DH 计算得到相同的 Pre-Master Secret。服务器在 ServerKeyExchange 中携带 DH 参数并使用私钥签名,防止中间人篡改。
Master Secret 派生: 双方使用 PRF(伪随机函数)将 Pre-Master Secret 与 client_random、server_random 混合,生成 48 字节的 Master Secret。
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)[0..47]
Master Secret 随后被用于派生会话密钥:客户端写密钥、服务器写密钥、初始化向量(IV)等。
4.6 Finished 消息 —— 握手完整性验证
Finished 消息是握手过程的最后一道防线,用于验证整个握手消息序列未被篡改。它是在 ChangeCipherSpec 之后发送的第一条加密消息。
计算方式:
verify_data = PRF(master_secret, "finished", handshake_messages)[0..11]
其中 handshake_messages 是截至(但不包括)Finished 消息之前所有握手消息的串联哈希值(使用协商的哈希算法,如 SHA-256)。
- 客户端 Finished: 客户端发送加密的 verify_data,服务器解密后验证是否与本地计算一致。
- 服务器 Finished: 服务器发送加密的 verify_data,客户端解密后验证。
如果 verify_data 不匹配,说明握手过程中发生了以下情况之一:
- 消息被中间人篡改(如修改了密码套件或 DH 参数)。
- 密钥派生出现错误(如 Pre-Master Secret 不一致)。
- 证书验证失败但客户端未终止握手。
4.7 握手状态机总结
下表总结了 TLS 1.2 完整握手中每条消息的发送方、接收方、加密状态和核心作用:
| 序号 | 消息 | 发送方 | 加密状态 | 核心作用 |
|---|---|---|---|---|
| ① | ClientHello | 客户端 | 明文 | 提交能力清单和随机数 |
| ② | ServerHello | 服务器 | 明文 | 选择参数并返回随机数 |
| ③ | Certificate | 服务器 | 明文 | 发送公钥证书链 |
| ④ | ServerKeyExchange | 服务器 | 明文 | 发送 DH/ECDH 参数及签名(仅 DHE/ECDHE) |
| ⑤ | ServerHelloDone | 服务器 | 明文 | 表示服务器消息结束 |
| ⑥ | ClientKeyExchange | 客户端 | 明文 | 发送 Pre-Master Secret 或 DH 公钥 |
| ⑦ | ChangeCipherSpec | 客户端 | 明文 | 通知后续消息将加密 |
| ⑧ | Finished | 客户端 | 加密 | 验证握手完整性 |
| ⑨ | ChangeCipherSpec | 服务器 | 明文 | 通知后续消息将加密 |
| ⑩ | Finished | 服务器 | 加密 | 验证握手完整性 |
openssl s_client -connect example.com:443 -tls1_2 -msg 命令查看完整的握手消息十六进制转储。结合 Wireshark 的 TLS 解析器,可以逐字段分析每条消息的内容,是学习 TLS 1.2 握手的最佳实践方式。