TLS/SSL 握手协议详解(四次握手)
HTTPS 的核心,其实是「HTTP over TLS」。咱们今天要聊的,就是这个 TLS 握手的完整过程。很多人觉得握手就是三次,其实在 TLS 1.2 及更早版本里,我们常说的是四次握手,或者叫「完全握手」。
我个人习惯把握手看作是在安全通信开始前,双方先互相认识、并且约定暗号的过程。你想想看,客户端和服务器要传敏感数据前,总得先确认对方身份、商量好怎么加密吧?嗯,这个过程,就是 TLS 握手要干的活。
一、握手协议的核心目标
握手阶段要解决三个问题:
- 身份认证:确认对方的真实身份,防止中间人冒充。
- 密钥协商:安全地生成一个只有双方知道的对称密钥,用于后续数据传输的加密。
- 参数协商:确定使用哪些加密算法、协议版本等。
一个很形象的比喻:这就是双方在互相对暗号。握手成功,后面就可以安心的脱裤子(传输内容加密)了。
二、四次握手流程详解
为了讲清楚,我把流程画出来。这张图是做嵌入式水印设计时我反复要看的东西——你得知道哪个时机点最适合嵌入。
这张图里我把握手浓缩为 4 个关键步骤。但实际上,每个步骤内部还藏着多个子消息。以前我接手的性能调优项目里,就有人把服务端证书链过大导致握手耗时长的坑踩了好几次。嗯,下文我会把这些坑拆开揉碎了讲。
三、步步详解握手过程
第一步:ClientHello
客户端主动发起,发送一些基本参数给服务端:
- Client Version:客户端支持的最高 TLS 版本。
- Client Random:28字节随机数,用于后续密钥生成。说白了就是「我这边先丢个随机种子给你」。
- Session ID:用于会话恢复。如果之前有握手过,可以带上上次的 session id,尝试快速恢复。
- Cipher Suites:客户端支持的密码套件清单(比如 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)。
- Extensions:扩展字段,比如 SNI(Server Name Indication)、ALPN 等。
我曾见过不少客户端在 SNI 扩展里不填写域名,导致服务器无法返回正确的证书——然后直接抛出证书警告。
第二步:ServerHello + 服务器数字证书 + ServerHelloDone
服务端收到 ClientHello 后进行决策,然后回复三个重要信息:
- ServerHello:返回服务端选择的 TLS 版本、密码套件(取交集)、服务端随机数。
- Certificate:服务端将数字证书(X.509 格式)发给客户端。
- ServerHelloDone:通知客户端「我发完了,你继续。」
曾经有一个部署事故,是因为服务器证书链没包含中间 CA,仅发了叶子证书。客户端收到后无法验证到受信根,握手直接中断。所以,证书链一定要完整交付。
第三步:ClientKeyExchange + ChangeCipherSpec + Finished
这一步客户端要确认对方的身份——验证服务器证书的有效性。验证通过后,继续发起:
- ClientKeyExchange:客户端生成 Pre-Master Secret(预主密钥),用服务端的公钥加密后传过去。
- ChangeCipherSpec:通知服务端「我要切换加密模式了,接下来发的内容都是加密的」。
- Finished:客户端发送一个加密的 Finished 消息,内容是之前所有握手消息的摘要。
很多人会混淆 Pre-Master Secret 和 Master Secret。这里我直接给公式:
Master Secret = PRF(Pre-Master Secret, "master secret", ClientRandom + ServerRandom)[0..47]
为什么搞得这么复杂?说白了就是让最终的对称密钥不依赖单一随机源。既能防重放,又能及时失效。
第四步:ChangeCipherSpec + Finished
服务端收到客户端加密的 Finished 后,用自己的私钥解密得到 Pre-Master Secret。接着,服务端也:
- ChangeCipherSpec:通知客户端「我也要加密通信了」。
- Finished:发送加密的 Finished 消息。
客户端收到后验证消息完整性。如果验证通过,握手就结束了。之后,双方就使用协商好的对称密钥进行加密传输。
你想想看,四次握手中最脆弱的环节是哪?嗯,是第三步——公钥加密 Pre-Master Secret 的时候。如果服务端私钥泄露,那整个加密形同虚设,所以密钥管理要上硬件。
四、核心知识点对比
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 握手阶段使用 | 不直接使用(用于后续传输) | 用于 Pre-Master Secret 传输 |
| 性能 | 快,适合大块数据 | 慢,约慢 100-1000 倍 |
| 密钥管理 | 需提前共享密钥 | 无需共享,公钥公开 |
那为什么不全程用非对称?很简单:对于一次性握手几个字节的密钥还可以,要是每次视频流都做公钥运算,服务器直接烧掉。以前我压测时就见过,一台普通服务器纯非对称握手只能扛 300 QPS,换成对称后直接翻十几倍。
五、实际部署中的避坑指南
我自己总结了一个常用密码套件排序建议:
- ECDHE + RSA + AES-128-GCM + SHA256(首选)
- ECDHE + ECDSA + AES-128-GCM + SHA256(有自带证书链时)
- 避免使用 CBC 模式的套件,TLS 1.2 后已不建议。
关于服务端证书:
- 证书有效期不要超过 398 天(Apple 和 Google 都已经限制)
- 记得启用 OCSP Stapling,否则每次连接客户端都可能去 CA 查询证书状态,增加握手延迟。
- ECDSA 证书比 RSA 证书短、计算也更快,但兼容性稍差。
我还记得有一次,项目里有人在服务端配置了 TLS 1.0,结果 APP 客户端强行要求 TLS 1.2,握手直接降级失败。所以,服务端至少要支持 TLS 1.2 以上。
六、知识点清单:四次握手小结
- 客户端随机数 + 服务端随机数 + Pre-Master Secret → 对称密钥
- Finished 消息:双向握手完整性校验,缺一不可
- ChangeCipherSpec:只是一个「我要切换了」的信号,本身不承载密钥
- Session ID / Session Ticket:用于简化后续握手(这篇暂时不讲)
- 证书链:不能缩短,必须包含根 CA 到叶子证书的完整链条
搞明白这四次握手,以后我们在做 HTTPS 水印嵌入的时候,你就知道该在哪一步动手了。说白了,握手的每个阶段都有 payload 或者扩展字段,这些就是我们的切入点。