本节摘要:SOURCE 3.2 逐步描述 ClientHello → ServerHello → Certificate → Key Exchange → ChangeCipherSpec → Finished。TLS 1.3 合并部分消息。
ssl handshake failure 无详细日志用 OpenSSL 客户端复现并打印协商详情:
openssl s_client -connect example.com:443 -servername example.com -tls1_2
观察 Cipher 行与 Verify return code——SOURCE 3.2 强调 Certificate Verification 与 Key Exchange 两步最易踩坑。

⚠️ 常见坑:中间设备做 SSL inspection 但未安装企业根——客户端报不可信 CA。
💡 关键直觉:握手失败先分「证书问题」还是「套件不匹配」。
把 TLS 1.2 的六步握手逐一对上安全目标,就能看清协议的严谨:ClientHello 里客户端随机数 + 服务端随机数共同参与密钥派生,防止重放;Certificate 消息携带服务器证书,客户端验证链与域名,对应「认证」目标;ServerKeyExchange 里的 ECDHE 临时公钥保障前向安全;Finished 消息对双方都哈希了所有握手消息,任何中间篡改都会导致验证失败,对应「完整性」;ChangeCipherSpec 之后的记录层用 AES-GCM 加密,对应「机密性」。每一步都不是摆设。
排错时的第一直觉应该是「分大类」:连接被重置多半是协议版本或套件不匹配;证书警告多半是链、域名或有效期问题;握手超时可能是 SNI 或中间设备干扰。openssl s_client -connect host:443 输出里 Verify return code 这一行直接给出证书验证失败的具体原因,是排错最快的入口。
# 握手失败常见错误速查 报错/现象 大类 常见根因 no shared cipher 套件不匹配 服务端/客户端套件无交集 certificate verify failed 证书 链不完整 / 自签 / 域名不符 handshake failure 协商失败 版本不兼容 / SNI 缺失 connection reset 网络/中间件 防火墙重置 / 无 TLS 端口 timeout 网络/SNI SNI 未传 / 虚拟主机无证书
把这张速查表放进运维手册,大部分「TLS 连不上」的工单可以在一分钟内定位到大类,再用具体命令(查套件、查链、查域名)收敛到根因。这也正是本教程以故障场景为线索组织内容的原因——把协议知识挂在真实报错上,记忆与复用都更高效。
# TLS 1.2 完整握手消息序列 客户端 -> 服务器 ClientHello 版本、套件列表、随机数、SNI 服务器 -> 客户端 ServerHello 选定版本与套件、服务器随机数 服务器 -> 客户端 Certificate 证书链 服务器 -> 客户端 ServerKeyExchange ECDHE 临时公钥 + 签名 服务器 -> 客户端 ServerHelloDone 客户端 -> 服务器 ClientKeyExchange 客户端密钥份额 客户端 -> 服务器 ChangeCipherSpec 切换到加密状态 客户端 -> 服务器 Finished 握手摘要的加密验证 服务器 -> 客户端 ChangeCipherSpec 服务器 -> 客户端 Finished 双方 Application Data 用协商密钥加密数据 # TLS 1.3 握手(合并后) 客户端 -> 服务器 ClientHello 含 key_share 扩展 服务器 -> 客户端 ServerHello + 加密的扩展 + 证书 + Finished 客户端 -> 服务器 Finished 双方 Application Data 1-RTT 完成 # 关键扩展与风险提示 SNI(server_name) 虚拟主机选择证书;缺失会导致拿错证书 ALPN 应用协议协商;HTTP/2 依赖它 key_share 1.3 的密钥份额;保证前向安全 0-RTT 重放风险高,默认关闭更稳妥
对照表展示了 TLS 1.3 合并消息的本质:把密钥交换、证书与收尾压缩进更少的往返,同时通过加密的握手消息保护元数据。理解两者差异,就能看懂「为什么升级 1.3 会快、会安全」这两条宣传语背后的协议机制。
再补充一个排查细节:握手失败时的日志位置往往比报错本身更重要。OpenSSL 客户端输出的最后几行,特别是 Verify return code 与 Cipher is 两行,一个回答「证书是否可信」,一个回答「最终协商了哪个套件」;而 Nginx 等服务器端的 error log 会给出握手失败所处的阶段。把「客户端视角」与「服务端日志」对照起来,就能判断失败发生在协商之前(版本/套件)、之中(密钥交换)还是之后(证书验证),对应的处理方式完全不同。这是把握手六步知识真正用起来的关键技巧。
另外,抓包分析时注意区分握手包与重传包:握手阶段的任何消息重传都意味着丢包或中间设备干预,经常被误判为「TLS 慢」或「证书问题」。先排除传输层干扰,再谈协议层配置,排障才不会南辕北辙。
还要记住 SNI 在虚拟主机环境的关键作用:同一个 IP 上托管多个域名时,服务器靠 ClientHello 里的 SNI 选择证书,SNI 缺失或与请求域名不一致会直接导致「拿错证书」或握手失败。