第3章:TLS 1.2握手详解——完整握手流程与密钥交换算法

终于聊到TLS 1.2握手了。说实话,我看过太多线上问题都出在这一步——握手延迟、秘钥协商失败、甚至直接断连。你想想看,每次HTTPS请求,背后都得跑一轮完整的握手,整个过程能有两三次往返时延(RTT)。要是网络不好,光握手就能吃掉几百毫秒。

我个人习惯先把握手拆成三个阶段:一是「你好」与参数协商,二是证书验证,三是密钥交换和确认。下面我们逐个分析,顺便聊聊RSA、DH、ECDHE这几个密钥交换算法在实际项目里踩过的坑。

3.1 完整握手流程(4次消息交互)

先画一张完整的握手套餐图。你对照着看,后面讲每一步会清晰很多。

TLS 1.2 完整握手流程(4步交互) 客户端(Client) 服务端(Server) ① ClientHello (TLS版本、密码套件列表、随机数) ② ServerHello + Certificate (选定套件、证书链、ServerHelloDone) ③ ClientKeyExchange (RSA:加密预主密钥 / DH:公钥) ④ ChangeCipherSpec + Finished (双方已协商好会话密钥,加密握手验证) ✅ 安全连接建立 说明:完整的四次握手共消耗 2-RTT。 如果采用会话复用(Session Ticket)可缩短为 1-RTT 甚至 0-RTT。 箭头方向:消息发送方向。

这张图里,第一步ClientHello带上客户端支持的TLS版本、密码套件列表、以及一个32字节的随机数。第二步ServerHello回应选定的套件、另一个随机数,再把证书(一般是X.509证书链)发过来。注意,证书里包含服务端的公钥,后面密钥交换全靠它。

小技巧: 线上抓包时,你可以用Wireshark过滤 tls.handshake.type == 1 快速定位ClientHello,看SSL/TLS版本和套件顺序。我曾在一家电商平台排查过握手失败,发现客户端默认用的是TLS 1.0,而服务端只支持TLS 1.2以上——握手直接拒了,页面白屏。

3.2 密钥交换算法:RSA、DH、ECDHE

第三步最关键:ClientKeyExchange。不同的密钥交换算法,这一步做的事情完全不一样,直接影响安全性和握手性能。

3.2.1 RSA密钥交换

RSA时代的做法:客户端生成一个48字节的Pre-Master Secret,用服务端证书里的RSA公钥加密,然后发给服务端。服务端用自己的私钥解密,拿到Pre-Master Secret。

听起来简单对吧?但有个致命问题:前向安全性为零。为什么?私钥一旦泄露,以前所有会话的Pre-Master Secret都能被解密,整个通信记录都暴露了。

警告: 截至2024年,主流浏览器已完全废弃基于RSA的密钥交换套件(如TLS_RSA_WITH_AES_128_CBC_SHA)。如果你还在用,建议立即迁移到ECDHE套件。

我在一个金融项目中维护过老系统,用的就是RSA交换。后来安全审计要求必须支持前向安全,我们花了整整一周升级到ECDHE——但那是后话了。

3.2.2 DH(Diffie-Hellman)密钥交换

DH算法让双方在不传递密钥的情况下,各自生成私钥和公钥,交换公钥,然后各自计算出相同的共享密钥。缺点是:DH参数固定(静态DH),服务端私钥依然有泄露风险;而且计算量大,握手延迟偏高。

其实DH很少单独用在TLS 1.2中,更多是被ECDHE取代了。

3.2.3 ECDHE(椭圆曲线Diffie-Hellman Ephemeral)

这是目前最推荐的算法。“Ephemeral”意为临时——每次握手生成一对临时的公私钥,用完就丢。就算这次握手的私钥泄露,也不会影响其他会话。前向安全性有保障。

ECDHE的计算量比传统DH小(同等安全强度下,椭圆曲线密钥更短),握手速度更快。常用曲线有P-256(secp256r1)和X25519。

算法 前向安全性 握手性能 当前推荐度
RSA ❌ 无 中等(公钥加密慢) ❌ 废弃
DH(静态) ⚠️ 部分(私钥泄露仍威胁) 低(大素数运算慢) ⚠️ 不推荐
ECDHE ✅ 强 高(椭圆曲线运算快) ✅ 强烈推荐

关键指标: ECDHE + ECDSA证书组合的握手延迟大约只有RSA交换方案的60%。我在《优化实践》项目中实测过:将密码套件从TLS_RSA_WITH_AES_256_CBC_SHA切换为TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256后,首次握手时间从200ms降到了120ms(同网络环境)。

3.3 完整握手消息序列(带代码示例)

下面是一个简化的TLS 1.2握手过程(ECDHE为例),你可以对照抓包工具理解。

Client → Server: ClientHello
  - TLS Version: 1.2
  - Cipher Suites: [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ...]
  - Random: 32 bytes

Server → Client: ServerHello
  - TLS Version: 1.2
  - Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  - Random: 32 bytes
Server → Client: Certificate (RSA证书链)
Server → Client: ServerKeyExchange (ECDHE参数:曲线、公钥、签名)
Server → Client: ServerHelloDone

Client → Server: ClientKeyExchange (ECDHE客户端的公钥)
Client → Server: ChangeCipherSpec
Client → Server: Finished (加密的握手消息摘要)

Server → Client: ChangeCipherSpec
Server → Client: Finished (验证成功)

// 之后双方开始加密通信

3.4 常见握手性能瓶颈与避坑

  • 证书链过长: 证书链超过3级(根→中间1→中间2→叶子)时,传输证书会多花一个RTT。我在某云平台上见过5级证书链,握手延迟直接多了80ms。
  • RSA加密大证书: RSA公钥加密的预主密钥是2048位的,加密耗时约1-2ms。但ECDHE只需要生成椭圆曲线密钥对(约0.1ms),差距很大。
  • 不支持Session Ticket: 每次都是完整握手,2-RTT。其实TLS 1.2支持会话复用,第一次握手后服务端发一个Session Ticket(加密的会话状态),以后恢复只需要1-RTT甚至0-RTT。

避坑指南: 我曾经遇到一个线上故障:客户端升级了OpenSSL版本,但服务器没升级。结果客户端倾向使用ECDHE套件,而服务器只支持RSA,握手失败。最终我们把服务器的密码套件列表重新排序,把ECDHE放在最前面,问题解决。

3.5 总结关键点

好,整理一下本章要点:

  1. 完整握手4次消息,共2-RTT。
  2. 密钥交换算法推荐ECDHE,放弃RSA。
  3. ECDHE提供前向安全,性能优于DH。
  4. 注意证书链长度和会话复用,能明显优化握手延迟。