TLS 握手过程详解归纳总结

说到 TLS 握手,我个人觉得这是整个加密通信里最精妙的一段。说白了,就是客户端和服务器在正式传数据之前,先互相打个招呼,确认身份,再商量好怎么加密。

我刚开始做网络审计那会儿,对这块也是一知半解。后来有一次抓包发现某个 HTTPS 站点一直握手失败,硬是折腾了一下午,才发现是服务器返回的证书链没带中间证书。从那以后,我对每个握手阶段的细节都格外留意。

握手的整体流程

TLS 握手可以分成几个清晰的阶段。你想想看,两个素未谋面的实体,要怎么安全地交换密钥?

  1. 协商阶段:客户端和服务器互相报上身份和支持的能力。
  2. 认证阶段:服务器出示证书,客户端验证。
  3. 密钥交换阶段:双方用非对称密钥安全地协商出一个对称密钥。
  4. 完成阶段:双方确认握手成功,开始用对称密钥加密通信。

核心要点:TLS 1.2 握手中,ClientHello 和 ServerHello 负责握手参数选择,Certificate 用于身份证明,ClientKeyExchange 携带预主密钥(Pre-master Secret),剩下的 ChangeCipherSpec 和 Finished 是完整性校验。

这里我放一张完整的流程图,方便你对照每一步。

TLS 1.2 握手流程(不包括会话恢复) 客户端 (Client) 服务器 (Server) ClientHello ServerHello Certificate ServerKeyExchange ServerHelloDone 验证证书 生成 Pre-master Secret ClientKeyExchange ChangeCipherSpec Finished 验证 Finished ChangeCipherSpec Finished 注意:握手完成后,两端通过 Master Secret 派生出会话密钥,开始对称加密通信。

ClientHello —— 客户端打招呼

客户端一上来先发个 ClientHello。里面带了啥?

  • 客户端支持的 TLS 版本(比如 1.2、1.3)
  • 一个 32 字节的随机数 ClientRandom,后面生成密钥要用
  • 支持的密码套件列表,比如 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • 可能还带一些扩展,比如 SNI(服务器名称指示)

嗯,这里要注意:如果客户端和服务器支持的版本有交集,才会继续。我在审计时见过一个案例,服务器只支持 TLS 1.0,但客户端已经禁掉了,结果一直在握手失败。

ServerHello 与服务器回应

服务器收到 ClientHello 后,会从里面挑一套双方都支持的参数,发回 ServerHello。内容包括:

  • 选定的 TLS 版本
  • 自己的 32 字节随机数 ServerRandom
  • 选中的密码套件

紧跟着就是 Certificate 消息,把服务器证书丢过来。如果是双向认证,还会要求客户端证书。接着可能发 ServerKeyExchange(比如 ECDHE 时需要传递椭圆曲线参数),最后发 ServerHelloDone 表示我知道的你都说完了。

经验:我遇到过有些老旧服务器居然在 ServerKeyExchange 里用了弱 Diffie-Hellman 参数(比如 512 位),这种握手就该直接拒绝。TLS 中间人攻击往往就钻这种参数的漏洞。

证书验证 —— 你的身份靠谱吗?

客户端拿到证书后,会做一系列检查:

  1. 证书是否在有效期内?
  2. 颁发者(CA)是否可信?一般会检查系统根证书库。
  3. 证书公钥是否和服务器私钥匹配?(通过验证证书签名)
  4. 域名是否和证书中的 CN/SAN 一致?

这步要是没通过,浏览器会弹大红叉。但在审计场景里,有些人就故意跳过证书验证来抓包,那安全就不存在了。

密钥交换 —— 最核心的一步

验证完证书后,客户端要生成一个 Pre-master Secret,然后用服务器的公钥加密发过去。如果用的是 ECDHE,客户端还会用自己的临时私钥生成一个共享秘密。具体流程:

步骤 客户端动作 服务器动作
1生成随机 Pre-master Secret(46 字节)—
2用服务器证书公钥加密 Pre-master Secret—
3发送 ClientKeyExchange(包含加密后的 Pre-master)用自己的私钥解密得到 Pre-master Secret
4双方用 ClientRandom + ServerRandom + Pre-master Secret 算出 Master Secret同样算出 Master Secret
5从 Master Secret 衍生出对称密钥(加密密钥、HMAC 密钥、IV 等)同理衍生

警告:如果使用 RSA 密钥交换,Pre-master Secret 是直接用 RSA 公钥加密的,一旦私钥泄露或者被中间人替换公钥,整个会话就被破解了。这也是为什么 ECDHE 更安全——它提供了前向保密(Forward Secrecy)。

Change Cipher Spec 与 Finished —— 一切就绪

客户端算好密钥后,先发一个 ChangeCipherSpec,告诉服务器“我要开始用新密钥说话了”。接着发 Finished 消息,里面是之前所有握手消息的 HMAC 值。服务器收到后,也要用 ChangeCipherSpec + Finished 回应。双方都验证 Finished 无误,握手才算成功。

我记得有一次审计,客户端发的 Finished 消息总是校验失败。后来才发现,是因为服务器的时钟偏差导致 ServerRandom 里带的 gmt_unix_time 不正确,影响了密钥派生。

握手阶段汇总

我整理了一个对照表,方便你看到每个消息的作用:

消息 方向 关键内容
ClientHelloC → S版本、随机数、密码套件、扩展
ServerHelloS → C选定版本和密码套件、随机数
CertificateS → C服务器证书(链)
ServerKeyExchangeS → CDH/ECDH 参数(如需要)
ServerHelloDoneS → C服务器消息结束
ClientKeyExchangeC → S加密后的 Pre-master Secret 或 DH 公钥
ChangeCipherSpecC ↔ S切换加密模式
FinishedC ↔ S握手完整性校验,用对称密钥加密

理解要点:TLS 握手本质上是利用非对称加密安全地传递一个随机种子(Pre-master Secret),然后借助伪随机函数(PRF)生成大量会话密钥。弄明白这个流程,后续讲中间人攻击的时候,你就能知道攻击者到底在哪里插了一脚。