TLS握手过程详解

说起TLS握手,很多同学第一反应就是“客户端发个Hello,服务端回个Hello”。嗯,本质上没错,但里面门道不少。我在项目里就踩过坑——有一次证书链没配全,导致握手卡在第三步,排查了大半天。说白了,握手过程就是双方互相确认身份、协商加密参数、建立安全通道。咱们一步步拆开看。

ClientHello:客户端发起请求

握手由客户端主动发起。它发送一个ClientHello消息,里面装着客户端支持的能力列表。

  • 支持的TLS版本:比如TLS 1.2、1.3,客户端会把能用的都列上。
  • 加密套件列表:一堆密码组合,按优先级排好。
  • 随机数:28字节的随机值,后面生成密钥要用。
  • 扩展字段:SNI(服务器名称指示)、ALPN(应用层协议协商)等。
我个人的习惯:客户端发送的加密套件列表,首选的尽量放强安全的。我之前见过有人把NULL加密套件排在前面,握手是成功了,但数据是明文传输…安全审计直接红牌。

ServerHello:服务器响应与选择

服务端收到ClientHello后,从客户端提供的列表里挑一个它最满意的组合。然后回复ServerHello。

ServerHello字段 说明
TLS版本 最终选定的版本,比如TLS 1.2
加密套件 选中的套件,比如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
随机数 服务端生成的32字节随机值
Session ID 用于会话复用,如果支持的话

证书验证:信任的基石

这一步很关键。服务端把自己的证书链发过来,客户端要验证是否可信。

  1. 证书链传递:服务端发送从服务器证书到根CA的完整链。
  2. 有效期检查:证书是否在有效期内。过期?直接断连。
  3. 域名匹配:证书里的CN或SAN字段,必须与客户端访问的域名一致。
  4. 签名验证:用上级CA的公钥验证下级证书的数字签名。
  5. 吊销检查:通过CRL(证书吊销列表)或OCSP(在线证书状态协议)检查证书是否被吊销。
我曾经处理过一个线上事故:某个服务的证书链只发了服务器证书,没有中间CA证书,客户端本地又没缓存,导致握手失败。记住,证书链必须完整,别偷懒。

密钥交换:安全隧道的关键

握手的前三步是握手阶段1,到这里正式开始协商共享密钥。TLS 1.2里主流用的是ECDHE(椭圆曲线Diffie-Hellman临时密钥交换)。

  • ServerKeyExchange:服务端发送它的椭圆曲线参数和公钥。
  • ClientKeyExchange:客户端发送自己的公钥。
  • 计算预主密钥:双方用对方的公钥和自己的私钥,通过ECDH算法计算出相同的预主密钥。
  • 派生主密钥:利用预主密钥加上之前交换的随机数,通过PRF(伪随机函数)生成会话密钥。

为什么用ECDHE而不是RSA?说白了,ECDHE支持前向安全性——即使私钥泄露,历史会话也无法被解密。我建议能上ECDHE就上,别用RSA静态密钥交换。

加密通信:数据保护的最后一步

密钥交换完成后,双方发送ChangeCipherSpec消息,告诉对方“我要开始加密了”。然后发送Finished消息,里面是对整个握手过程的MAC值。

  • ChangeCipherSpec:一个协议级别的标志,不是握手消息的一部分。
  • Finished:用协商好的密钥加密整个握手摘要,双方验证一致,握手才算成功。
  • 应用数据:从这开始,所有HTTP请求/响应都经过对称加密传输。
关键点:握手过程中的任何一步失败,连接直接断开。日志里出现"handshake failure"或"alert",大多是这里出问题。我个人排查时,会从证书开始看,然后对比随机数和密钥交换参数。

对了,TLS 1.3的握手更简洁,把ServerHello和Certificate合并了,还移除了静态RSA和CBC模式。但不管哪个版本,这五个大阶段是固定的。理解了这个流程,你再看日志里的握手异常,基本能一眼定位。

TLS 1.2 完整握手流程 客户端 ① ClientHello ④ 证书验证 ⑤ ClientKeyExchange ⑥ ChangeCipherSpec ⑧ 加密应用数据 服务端 ② ServerHello ③ 证书链 ⑤ ServerKeyExchange ⑦ ChangeCipherSpec ⑧ 加密应用数据 密钥协商完成,派生主密钥 阶段1:协商 客户端参数 + 服务端选择 + 证书验证 阶段2:密钥交换 ECDHE参数 + 预主密钥 + 派生会话密钥 阶段3:加密通信 ChangeCipherSpec + 加密应用数据