第3章:TLS 1.2握手详解——完整握手流程与密钥交换算法
终于聊到TLS 1.2握手了。说实话,我看过太多线上问题都出在这一步——握手延迟、秘钥协商失败、甚至直接断连。你想想看,每次HTTPS请求,背后都得跑一轮完整的握手,整个过程能有两三次往返时延(RTT)。要是网络不好,光握手就能吃掉几百毫秒。
我个人习惯先把握手拆成三个阶段:一是「你好」与参数协商,二是证书验证,三是密钥交换和确认。下面我们逐个分析,顺便聊聊RSA、DH、ECDHE这几个密钥交换算法在实际项目里踩过的坑。
3.1 完整握手流程(4次消息交互)
先画一张完整的握手套餐图。你对照着看,后面讲每一步会清晰很多。
这张图里,第一步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 总结关键点
好,整理一下本章要点:
- 完整握手4次消息,共2-RTT。
- 密钥交换算法推荐ECDHE,放弃RSA。
- ECDHE提供前向安全,性能优于DH。
- 注意证书链长度和会话复用,能明显优化握手延迟。