首先想到TLS握手失败的关键排查点:证书链不完整、域名不匹配、证书过期、根证书不受信任、协议版本不兼容这些常见原因。Wireshark的优势就在于能直观展示握手全过程的每个报文,特别是Server Hello和Certificate那两个关键帧。使用 Wireshark 分析 SSL证书握手失败(特别是证书相关失败)是排查 HTTPS、IMAPS、SMTPS 等加密连接问题的关键手段。以下是详细步骤和要点:
核心步骤概览
捕获流量
设置 Wireshark 解密 TLS (关键步骤)
过滤并定位 TLS 握手过程
分析 Server Hello 和 Certificate 消息
查找 Alert 消息 (握手失败标志)
解读错误原因
详细步骤
1. 捕获流量
启动 Wireshark,选择合适的网络接口(如 eth0, wlan0)。
开始捕获。
复现问题:在客户端发起会触发 SSL/TLS 握手失败的操作(如访问问题网址)。
捕获完成后停止。
2. 设置 Wireshark 解密 TLS (最重要的一步)
Wireshark 默认无法解密 TLS 应用层数据(包括握手细节中的证书信息)。要让 Wireshark 解密 握手过程(特别是查看证书内容),必须提供会话密钥。常用方法:
方法 A:使用 SSLKEYLOGFILE 环境变量 (推荐)
在客户端系统设置环境变量:
Windows (CMD/PowerShell):
cmd
set SSLKEYLOGFILE=C:\path\to\premaster.txt
Linux/macOS (Bash):
bash
export SSLKEYLOGFILE=/path/to/premaster.txt
重启使用该环境变量的浏览器/应用程序 (如 Firefox, Chrome, curl)。
在 Wireshark 中设置密钥文件:
Edit -> Preferences -> Protocols -> TLS (或 SSL)。
在 (Pre)-Master-Secret log filename 字段,浏览并选择上面设置的 premaster.txt 文件。
重新捕获流量(或重新加载现有捕获文件)。Wireshark 现在可以解密握手过程和证书信息。
方法 B:提供服务器私钥 (仅解密入站流量,适用于你拥有服务器私钥的情况)
Edit -> Preferences -> Protocols -> TLS。
在 RSA keys list 区域,点击 Edit。
添加一个新条目:
IP address: 服务器的 IP 地址(或留空 0.0.0.0 匹配所有)。
Port: 服务器的 TLS 端口 (如 443)。
Protocol: http (或其他合适的协议,如 imap, ftp 等,通常 http 即可)。
Key File: 浏览并选择服务器的私钥文件 (如 .key, .pem)。
Password: 如果私钥有密码,填写密码。
点击 OK -> OK。重新加载捕获文件或重新捕获。
3. 过滤并定位 TLS 握手过程
在 Wireshark 的过滤栏输入:
tls: 显示所有 TLS 流量。
tls.handshake.type == 1: Client Hello (客户端发起握手)。
tls.handshake.type == 2: Server Hello (服务器响应,包含选定的协议/密码套件)。
tls.handshake.type == 11: Certificate (服务器发送其证书链)。
tls.handshake.type == 15: Certificate Verify (客户端证书身份验证时使用)。
tls.handshake.type == 16: Client Key Exchange。
tls.record.content_type == 21: Alert (握手失败或连接关闭的关键信号!)。
分析流程:
找到 Client Hello (tls.handshake.type == 1)。
找到紧随其后的 Server Hello (tls.handshake.type == 2)。查看服务器选择的协议版本 (TLS 1.2? 1.3?) 和密码套件。
关键:找到 Certificate 消息 (tls.handshake.type == 11)。这是服务器发送证书的地方。
4. 分析 Certificate 消息 (服务器证书)
在数据包列表中选择类型为 11 (Certificate) 的数据包。
在数据包详情面板中,展开 Transport Layer Security -> TLSv1.x Record Layer: Handshake Protocol: Certificate -> Handshake Protocol: Certificate。
展开 Certificates 列表: 这里会显示服务器发送的证书链(通常包括服务器证书、一个或多个中间 CA 证书)。
逐个检查每个证书:
证书链完整性: 检查是否提供了完整的链?最后一个证书是否是客户端信任的根 CA 证书?还是只发送了服务器证书?
Certificate 条目右键 -> Export Packet Bytes... 可以将证书导出为 .der 文件,用其他工具(如 openssl x509 -inform der -in cert.der -text -noout) 详细查看。
在 Wireshark 中直接查看关键字段 (展开每个 Certificate 下的 Signed Certificate):
Issuer: 颁发此证书的 CA。
Validity: Not Before 和 Not After。检查证书是否过期!
Subject: 证书持有者信息。重点检查 Common Name (CN) 和 Subject Alternative Name (SAN) 扩展:
域名匹配问题: 证书的 CN 或 SAN 是否包含了客户端实际访问的主机名?例如,访问 www.example.com,但证书只签发给 example.com(缺少 www)或 *.example.com(但访问的是 subdomain.example.com 而 subdomain 不在通配符范围内)。这是非常常见的失败原因。
Extensions -> Authority Key Identifier / Subject Key Identifier: 用于验证证书链的链接关系。
Extensions -> Basic Constraints: 是否是 CA 证书?
Extensions -> Key Usage / Extended Key Usage: 证书用途是否正确(如 TLS Web Server Authentication)?
签名验证 (Wireshark 可能直接提示): 在证书详情中查找是否有类似 [Unable to get local issuer certificate] 或 [Invalid certificate signature] 的警告。这通常表明证书链不完整或根证书不受信任。
5. 查找 Alert 消息 (握手失败标志)
这是确定握手失败原因的最直接证据!
过滤:tls.record.content_type == 21 或直接看 Client Hello 之后是否很快出现了类型为 Alert (21) 的数据包,且其 Severity 通常是 Fatal (2)。
分析 Alert 包:
在数据包详情面板中,展开 Transport Layer Security -> TLSv1.x Record Layer: Alert (Level: Fatal, Description: ...)。
关键字段:
Level: Fatal (2) 表示严重错误,握手将终止。
Description: 最重要的字段! 明确指出了失败原因。常见的与证书相关的 Description 值:
bad_certificate (42): 证书格式错误、签名无效等一般性问题。
unsupported_certificate (43): 证书类型不受客户端支持(非常罕见)。
certificate_revoked (44): 证书已被吊销! (需检查 OCSP 或 CRL)。
certificate_expired (45): 证书已过期!
certificate_unknown (46): 证书不受信任! 最常见的原因之一:无法构建到受信任根 CA 的完整证书链(中间 CA 缺失或根 CA 不被客户端信任)。
unknown_ca (48): 颁发服务器证书的 CA 未知/不受信任。
access_denied (49): 可能涉及客户端证书验证失败。
decode_error (50): 解码证书时出错(格式损坏)。
handshake_failure (40): 更通用的握手失败,可能与证书相关(如密码套件协商失败也可能导致此错误,需结合上下文分析)。
注意方向: 是客户端发出的 Alert 还是服务器发出的 Alert?这有助于判断是哪一端拒绝了证书。通常客户端拒绝服务器证书是 certificate_unknown (46) 或 certificate_expired (45) 等。服务器拒绝客户端证书通常是 bad_certificate (42) 或 access_denied (49)。
6. 解读错误原因
结合 Alert 消息的 Description 和你在 Certificate 消息中观察到的具体问题(过期、域名不匹配、链不完整、吊销等),即可精确诊断 SSL 证书握手失败的根本原因。
常见原因总结:
证书链不完整/根证书不受信任: 服务器未发送所有中间 CA 证书,或者客户端的信任库(根证书列表)中缺少根 CA 或某个中间 CA。Alert: certificate_unknown (46) 或 unknown_ca (48)。
证书过期: Alert: certificate_expired (45)。检查证书的 Validity。
证书吊销: Alert: certificate_revoked (44)。需要检查 OCSP 响应或 CRL。
域名不匹配: 客户端访问的 URL 主机名与证书 CN 或 SAN 不匹配。通常会导致 Alert: bad_certificate (42) 或 certificate_unknown ,但核心问题是域名验证失败。仔细比对 Subject/SAN 和实际访问的主机名。
服务器配置错误: 未配置证书、配置了错误的证书/私钥对(导致签名验证失败)、使用了不受支持的协议版本或密码套件(可能导致 handshake_failure )。
客户端时间错误: 客户端系统时间严重错误(远早于 Not Before 或远晚于 Not After)会导致证书“过期”或“未生效”的错误。
自签名证书未导入信任库: 本质也是证书链不受信任 (certificate_unknown )。
Wireshark 截图要点示例 (文字描述)
Server Hello 包: 显示协商的 TLS 版本 (TLS 1.2) 和密码套件 (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)。
Certificate 包:
展开 Certificates 显示链长度(如 2 个证书:服务器证书 + 中间 CA)。
选中服务器证书,在详情面板查看:
Subject: CN = wrong.hostname.com (域名不匹配!)
Validity: Not After: Jan 01 2020 (证书过期!)
警告信息:[Unable to get local issuer certificate] (链不完整/中间 CA 缺失!)
Alert (Fatal) 包:
Level: Fatal (2)
Description: Certificate Unknown (46) 或 Certificate Expired (45) 或 Unknown CA 。
注意事项
解密是关键: 没有正确设置 SSLKEYLOGFILE 或提供服务器私钥,你将无法看到 Certificate 消息的明文内容和确切的 Alert 描述,分析将非常困难。
关注 Alert 消息: 这是服务器或客户端明确报告错误原因的地方,优先级最高。
结合其他工具: Wireshark 分析网络层行为。同时使用 openssl s_client -connect host:port -showcerts 或浏览器开发者工具 (Network/Security 标签) 可以更方便地从应用层获取证书信息和错误提示,两者结合分析更佳。
区分协议版本: TLS 1.3 的握手过程与 TLS 1.2 及更早版本有显著不同(更简洁,证书在 Server Hello 后立即发送),但核心的证书验证和 Alert 机制是相似的。
用户通过遵循以上几点步骤和要点,你就能有效地利用 Wireshark 定位并理解导致 SSL证书 握手失败的证书问题根源。