TLS 这家伙到底是干什么的

如果你每天都在跟 HTTPS 打交道,那你其实天天都在用 TLS。说白了,TLS 就是给网络通信加了一把锁。它保证三件事:别人偷看不了你的数据(机密性)、别人改不了你的数据(完整性)、你连接的那个服务器确实是它自己(身份认证)。

我刚开始接触网络安全时,总觉得这些概念很虚。直到有一次在项目里抓包,发现明文密码直接在网络上裸奔……嗯,那之后我才真正明白,TLS 不是可有可无的装饰品。

握手?这不是社交,这是密码学的第一次接触

TLS 握手,就是客户端和服务器在正式开始加密通信之前,互相认识一下、商量用什么密码、交换密钥的过程。你想想看,两个人要加密聊天,总得先定好用什么暗号吧?握手就干这个。

TLS 握手的核心目标其实就四个:

  • 协商密码套件 —— 双方选一个都支持的加密算法组合
  • 身份验证 —— 通常是服务器出示证书,证明“我是我”
  • 密钥交换 —— 安全地生成一个只有双方知道的对称密钥
  • 握手完成确认 —— 双方确认一切就绪,开始加密通信
一个容易被忽略的事实: 握手阶段的所有失败,超过 80% 都跟密码套件配置不当有关。你在排查 TLS 问题时,第一个要查的就是双方支持的密码套件列表。

我画了一张图,把全过程说清楚

下面这张图展示了 TLS 1.2 完整握手的核心流程。每一个箭头都代表一次网络交互,每一步都可能因为密码配置不对而卡住。

TLS 1.2 完整握手流程与密码故障高风险点 客户端 (Client) 服务器 (Server) ① ClientHello ⚠ 密码套件列表在此发送 ② ServerHello + 证书 + ServerHelloDone ⚠ 证书链验证失败高频区 ③ 验证证书合法性 域名 | 有效期 | 颁发者 ④ ClientKeyExchange ⚠ 密钥交换参数错/不匹配 ⑤ ChangeCipherSpec + Finished ⑥ ChangeCipherSpec + Finished 开始加密通信 — 数据用对称密钥保护 握手完成!所有后续数据都经过加密和完整性校验 💡 关键记忆点 握手每一步都可能因为密码套件不兼容、证书问题、密钥交换参数错误而失败。 排查时优先检查:① 双方密码套件交集 ② 证书链完整性 ③ 密钥交换算法匹配。

我们来拆解一下每一步

第一步:ClientHello

客户端先开口。 它告诉服务器:“我会这些密码套件,我支持这些 TLS 版本,我带了这些随机数。” 密码套件列表在这里特别关键——如果列表是空的,或者全是服务器不认识的算法,握手直接卡死。

我印象很深的一次线上事故,是因为某老版 JDK 默认启用了几十个密码套件,而服务器只支持 ECDHE 套件,结果两者没有交集……抓包一看,ClientHello 里一个能用的都没有。嗯,从那以后我写代码总是先打印双方支持的套件列表。

第二步:ServerHello 与证书

服务器做选择。 它从客户端的列表里挑一个它支持的密码套件,然后把自己的证书链扔过来。

这里有个常见的坑:证书链不完整。服务器只发了终端证书,没有发中间 CA 证书,客户端的证书校验就会失败。我曾经在调试一个 OpenSSL 客户端时,遇到了 certificate verify failed,折腾半天才发现服务器漏发了中间证书。

  • 服务器选择的密码套件必须出现在客户端的列表中
  • 证书链必须完整(终端证书 + 中间 CA 证书)
  • 证书的域名必须匹配客户端访问的域名

第三步:客户端验证证书

客户端拿到证书后,会做三件事:

  1. 检查证书是否在有效期内
  2. 验证数字签名是否由可信 CA 签发
  3. 确认证书中的域名与正在访问的域名一致

这三件事任何一件出问题,握手都会中断。你可能见过浏览器里那个大红锁,就是这会出的问题。

第四步:ClientKeyExchange 与密钥生成

这一步最容易因密码配置不当而出错。 根据双方协商的密钥交换算法,客户端可能发送预主密钥(RSA 模式)或者交换临时公钥(DH 模式)。

举个例子:如果服务器选了 ECDHE 套件,但客户端发送的椭圆曲线参数服务器不认识——握手直接失败。我在一些国产设备上就遇到过这种情况,它们的 OpenSSL 库编译时没启用某些曲线。

一个小技巧: 排查这类问题时,可以用 openssl s_client -connect example.com:443 -msg 命令。它会把你每次发送和接收的 TLS 消息都打印出来,你能清晰地看到密码套件协商、证书、密钥交换每一步的具体内容。

第五步与第六步:切换密码规格与握手完成

双方各自发送 ChangeCipherSpec 消息,告诉对方:“从现在起,我要用商量好的密码套件和密钥加密了。” 接着发送 Finished 消息,里面是对整个握手过程的 HMAC。如果双方计算的 HMAC 不一致——说明有人篡改了握手过程,或者密钥计算有误。

注意: Finished 消息失败几乎只发生在密钥计算错误的情况下。双方使用相同的密码套件、相同的随机数,理论上应该算出相同的密钥。如果这里出错,请优先检查 Pre-Master Secret 的随机数是否被正确使用,或者双方对密码套件的理解是否一致。

一个表格,把密码故障位置说清楚

握手阶段 可能的密码故障 典型表现
ClientHello 密码套件列表为空或不兼容 服务器返回握手失败 alert
ServerHello 选中的套件客户端不支持 客户端收到不认识的密码套件
证书验证 证书签名算法不匹配 certificate verify failed
ClientKeyExchange 密钥交换参数长度或曲线错误 decryption failed / bad ec parameters
Finished 校验 密钥派生错误 decrypt_error / bad record mac

看到这里,你应该已经明白了:TLS 握手本质上是一个密码协议的有序执行。每一步都依赖上一步的结果,每一步都可能因为密码配置不当而断裂。

在接下来的章节里,我们会逐一拆解每个密码套件组件——密钥交换、认证、加密、HMAC——看看它们到底是怎么工作的,出问题了怎么查。但今天先把握手的骨架打好,后面才好填充血肉。