3. TLS 1.2 握手协议详解:ClientHello 与 ServerHello、证书交换与密钥协商、Finished 消息验证
TLS 1.2 握手协议是安全通信的基石,负责在客户端与服务器之间协商加密参数、验证身份并生成共享密钥。本章将深入剖析握手协议的三个核心阶段:Hello 消息交换、证书与密钥协商、以及 Finished 消息的完整性验证。理解这些细节对于分析协议漏洞(如降级攻击、中间人攻击)至关重要。
3.1 ClientHello 与 ServerHello:密码套件协商与随机数交换
握手的第一步是客户端与服务器交换 Hello 消息,用于协商协议版本、密码套件、压缩算法以及交换随机数。这些随机数是后续密钥派生的重要输入。
3.1.1 ClientHello 消息结构
客户端发送 ClientHello 消息,包含以下关键字段:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Protocol Version | 2 | 客户端支持的最高 TLS 版本,TLS 1.2 为 0x0303 |
| Random | 32 | 客户端生成的 32 字节随机数(client_random),包含 4 字节 Unix 时间戳 + 28 字节随机值 |
| Session ID | 可变 | 用于会话恢复的标识符,若为空则表示新建会话 |
| Cipher Suites | 可变 | 客户端支持的密码套件列表,按优先级降序排列 |
| Compression Methods | 可变 | 支持的压缩算法列表,通常仅包含 null(0x00) |
| Extensions | 可变 | 扩展字段,如 SNI、ALPN、签名算法、椭圆曲线等 |
3.1.2 ServerHello 消息结构
服务器收到 ClientHello 后,选择双方都支持的参数,并回复 ServerHello:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Protocol Version | 2 | 服务器选择的 TLS 版本,不得高于客户端提供的版本 |
| Random | 32 | 服务器生成的 32 字节随机数(server_random) |
| Session ID | 可变 | 若支持会话恢复,则返回匹配的 Session ID;否则生成新 ID |
| Cipher Suite | 2 | 从客户端列表中选定的单一密码套件 |
| Compression Method | 1 | 选定的压缩算法,通常为 null |
| Extensions | 可变 | 服务器支持的扩展响应 |
3.1.3 Hello 消息交换流程
下图展示了 ClientHello 与 ServerHello 的交互过程,以及随机数在后续密钥派生中的作用:
3.2 证书交换与密钥协商:身份验证与预主密钥生成
在 Hello 消息交换之后,服务器(通常)会发送其数字证书,并可能请求客户端证书。随后,双方通过密钥交换算法(如 RSA 或 ECDHE)协商出预主密钥(Pre-Master Secret)。
3.2.1 证书交换过程
服务器发送 Certificate 消息,包含一个 X.509 证书链(从服务器证书到根 CA 证书)。客户端需要验证证书链的合法性:
- 证书链验证:检查每个证书的签名是否由上一级 CA 签发,直至信任的根 CA。
- 有效期检查:确认证书未过期且未被撤销(通过 CRL 或 OCSP)。
- 域名匹配:验证证书中的
subjectAltName或CN字段与服务器域名一致。 - 密钥用途:确认证书的密钥用途(Key Usage)包含数字签名或密钥加密。
3.2.2 密钥协商算法对比
TLS 1.2 支持多种密钥交换算法,其中 RSA 和 ECDHE 是最常见的两种。下表对比了它们的安全特性:
| 特性 | RSA 密钥交换 | ECDHE 密钥交换 |
|---|---|---|
| 前向安全性 | ❌ 不支持(私钥泄露可解密所有历史会话) | ✅ 支持(每次会话使用临时密钥对) |
| 密钥生成方式 | 客户端生成预主密钥,用服务器公钥加密后发送 | 双方通过 ECDH 协议协商出共享密钥 |
| 计算开销 | 较低(非对称加密一次) | 较高(需要椭圆曲线点乘运算) |
| 抗量子攻击 | ❌ 脆弱(Shor 算法可破解 RSA) | ❌ 脆弱(但优于 RSA) |
| 典型密码套件 | TLS_RSA_WITH_AES_128_CBC_SHA |
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
3.2.3 ECDHE 密钥协商流程
下图展示了基于 ECDHE 的密钥协商过程,包括服务器发送 ServerKeyExchange 消息携带临时公钥:
3.3 Finished 消息验证:握手完整性校验
在密钥协商完成后,双方交换 ChangeCipherSpec 消息(通知对方后续消息将使用协商的密钥加密),然后发送 Finished 消息。Finished 消息是握手过程的最终验证,确保所有握手消息未被篡改。
3.3.1 Finished 消息结构
Finished 消息包含一个 verify_data 字段,其计算方式如下:
verify_data = PRF(master_secret, "finished", Hash(handshake_messages)) [0..11]
其中:
- master_secret:由预主密钥和随机数派生的 48 字节主密钥。
- "finished":固定标签字符串。
- Hash(handshake_messages):所有握手消息(从 ClientHello 到 ServerHelloDone,不包括 Finished 本身)的哈希值。对于 TLS 1.2,使用密码套件指定的哈希算法(如 SHA-256)。
- [0..11]:取 PRF 输出的前 12 字节。
3.3.2 验证流程
- 客户端发送 Finished:客户端计算 verify_data,并使用当前加密参数加密后发送。
- 服务器验证:服务器解密后,使用相同的输入重新计算 verify_data,并与接收到的值比较。如果匹配,则握手成功;否则,发送
alert消息并终止连接。 - 服务器发送 Finished:服务器同样计算并发送自己的 verify_data。
- 客户端验证:客户端执行相同的验证步骤。
3.3.3 常见漏洞与防御
| 漏洞类型 | 攻击原理 | 防御措施 |
|---|---|---|
| 降级攻击 | 攻击者修改 ClientHello 中的版本号或密码套件,诱使服务器选择弱算法 | 服务器应拒绝低于 TLS 1.2 的版本;使用 TLS 1.3 的强制版本协商机制 |
| 证书伪造 | 攻击者使用伪造的证书冒充服务器 | 客户端严格验证证书链、域名匹配和吊销状态;使用证书透明度(CT) |
| 密钥协商篡改 | 攻击者修改 ServerKeyExchange 中的临时公钥 | 服务器对 ServerKeyExchange 消息进行数字签名;客户端验证签名 |
| Finished 消息重放 | 攻击者重放之前的 Finished 消息 | Finished 消息包含随机数,确保每次握手的 verify_data 唯一 |
- 使用安全的随机数生成器生成 client_random 和 server_random。
- 优先选择支持前向安全性的密码套件(如 ECDHE + GCM)。
- 在 Finished 验证失败时,立即终止连接并记录告警日志。
- 定期更新 CA 证书库,避免信任已泄露的根证书。
3.4 本章小结
TLS 1.2 握手协议通过 ClientHello/ServerHello 协商参数,通过证书交换和 ECDHE/RSA 密钥协商生成共享密钥,最后通过 Finished 消息验证握手完整性。每个阶段都引入了特定的安全风险:
- Hello 阶段:降级攻击和版本回滚。
- 证书阶段:证书伪造和中间人攻击。
- 密钥协商阶段:缺乏前向安全性的 RSA 交换。
- Finished 阶段:消息篡改检测失败。