两次往返的握手顺序
TLS 1.2 握手通常需要两次往返:客户端先发 ClientHello 提议协议版本、随机数与套件列表,服务端用 ServerHello 选定参数并下发证书,随后双方交换密钥材料,各自发送 ChangeCipherSpec 与 Finished 确认握手内容未被篡改,之后才开始传输应用数据。把这条消息序列记熟,抓包时就能一眼看出握手卡在哪一步,而不用靠猜。
流程骨架
流程骨架
Client -> Server:
ClientHello # 版本、客户端随机数、套件列表、扩展(含 SNI)
Server -> Client:
ServerHello # 选定版本与套件、服务端随机数
Certificate # 证书链,叶证书在前
ServerKeyExchange* # 仅 DHE 与 ECDHE 套件出现
ServerHelloDone # 服务端消息结束
Client -> Server:
ClientKeyExchange # 客户端交换密钥材料
ChangeCipherSpec # 之后的消息开始加密
Finished # 握手完整性校验
Server -> Client:
ChangeCipherSpec
Finished # 握手完成,开始传应用数据消息读法
| 消息 | 方向 | 含义 |
|---|---|---|
| ClientHello | 客户端到服务端 | 携带支持的版本、套件与 SNI,抓包里能直接看到访问的域名 |
| ServerHello | 服务端到客户端 | 确定最终版本与套件,双方后续都按这一组参数运算 |
| Certificate | 服务端到客户端 | 证书链,顺序错或缺中间证书会在这里暴露 |
| ServerKeyExchange | 服务端到客户端 | 只有 DHE 与 ECDHE 套件才有,是判断前向保密的直接依据 |
| ChangeCipherSpec | 双向 | 提示对端之后的消息开始加密 |
| Finished | 双向 | 校验整个握手过程,值不匹配则握手失败 |
典型使用场景
- 抓包定位握手失败:确认消息停在哪一步,区分是版本套件不兼容还是证书被拒
- 确认套件是否具备前向保密:看有无密钥交换消息以及临时密钥类型
- 排查 SNI 与证书不匹配:核对 ClientHello 里的域名与服务端下发的证书
- 分析中间设备干扰:对比两端抓包的握手消息序列是否一致
- 评估握手耗时:结合两次往返判断是网络问题还是服务端计算问题
抓握手
抓握手
openssl s_client -connect example.com:443 -msg -tls1_2 </dev/null 2>&1 | grep -E '>>>|<<<' | head -30 # 按方向列出握手消息
openssl s_client -connect example.com:443 -tls1_2 -brief </dev/null 2>&1 | head -10 # 只看协商结果
openssl s_client -connect example.com:443 -tls1_2 -showcerts </dev/null 2>/dev/null | grep -E 's:|i:' # 只看证书链选到 RSA 密钥传输套件时不会有密钥交换消息,这也正是它没有前向保密的原因:一旦私钥泄露,历史流量都可以被解密。ECDHE 套件才有临时密钥,私钥泄露也影响不到已完成的会话。
TLS 1.2 握手期间 SNI 与证书是明文可见的,抓包就能看出访问了哪个域名,这也是中间设备能做域名级策略的基础;TLS 1.3 把证书放进加密消息后,只剩 SNI 还会暴露。