3、故障排查工具:openssl命令行工具、keytool工具、curl与wget的证书验证、日志分析技巧
说实话,证书轮换出问题的时候,最怕的就是两眼一抹黑。你明明觉得配置没问题,但服务就是报错。这时候,手里有几把趁手的工具就特别重要了。
我个人习惯,排查证书问题从来不用图形界面。命令行工具才是王道。它们直接、快速,而且能让你看到最原始的数据。今天我就把压箱底的几样工具拿出来,跟你好好聊聊。
核心思路: 证书问题的本质是信任链断裂或格式不匹配。所有工具都是围绕「验证证书内容」和「验证信任链」这两个点展开的。
3.1 openssl:瑞士军刀,没有之一
openssl 是我用得最多的工具。它几乎能解决所有跟证书相关的疑问。你想想看,一个命令就能把证书的里里外外看个透。
3.1.1 查看证书详情
拿到一个 .crt 或 .pem 文件,第一件事就是看它的内容。别信文件名,信命令输出。
# 查看 PEM 格式证书
openssl x509 -in server.crt -text -noout
# 查看 DER 格式证书(Windows 常见)
openssl x509 -in server.der -inform DER -text -noout
输出里重点看这几个字段:
- Subject:证书颁发给谁
- Issuer:谁颁发的
- Validity:有效期,Not Before 和 Not After
- Subject Public Key Info:密钥算法和长度
- X509v3 extensions:特别是 Subject Alternative Name (SAN)
我的习惯: 每次轮换后,我都会用这个命令对比新旧证书的序列号。如果序列号一样,说明你根本没换成功。我曾经遇到过运维同事跑错了脚本,新旧证书是同一张,排查了半天。
3.1.2 验证证书链
单张证书没问题,不代表链没问题。这是最坑的地方。
# 验证证书链
openssl verify -CAfile ca-chain.pem server.crt
# 如果有多级中间证书
openssl verify -CAfile root.pem -untrusted intermediate.pem server.crt
输出 server.crt: OK 才算通过。如果报错,常见的有:
unable to get local issuer certificate:找不到颁发者证书,链不完整certificate has expired:证书过期了self-signed certificate in chain:链里混进了自签名证书
注意: openssl verify 默认不检查 SAN 和 CN 匹配。它只检查证书链的合法性。域名匹配要用其他方法。
3.1.3 测试 SSL/TLS 连接
服务端证书到底有没有生效?直接连上去看看。
# 连接服务并显示证书
openssl s_client -connect example.com:443 -servername example.com
# 指定 CA 证书验证
openssl s_client -connect example.com:443 -CAfile ca-chain.pem
# 显示完整证书链
openssl s_client -connect example.com:443 -showcerts
-servername 参数很重要。SNI(Server Name Indication)依赖它。不加这个参数,你可能拿到的是默认证书,而不是你想要的。
我记得有一次,客户说证书轮换后手机端报错,但浏览器正常。我用 openssl s_client 一测,发现不传 SNI 时返回的是旧证书。原来是负载均衡器配置没更新,SNI 匹配规则有问题。
3.2 keytool:Java 生态的守门员
如果你跟 Java 应用打交道,keytool 是绕不开的。它管理的是 Java KeyStore (JKS) 或 PKCS12 格式的密钥库。
3.2.1 查看密钥库内容
# 查看 JKS 内容
keytool -list -v -keystore keystore.jks -storepass changeit
# 查看 PKCS12 内容
keytool -list -v -keystore keystore.p12 -storetype PKCS12 -storepass changeit
输出会列出所有条目,包括:
- 别名(Alias)
- 条目类型(PrivateKeyEntry / trustedCertEntry)
- 证书指纹(MD5、SHA1、SHA256)
- 有效期
避坑指南: 我曾经遇到过开发人员把私钥和证书链分开导入,结果 alias 不一致,应用启动报错。建议统一使用一个 alias,比如用域名。
3.2.2 导入导出证书
# 导出证书
keytool -exportcert -alias mydomain -keystore keystore.jks -file mydomain.cer
# 导入信任证书
keytool -importcert -alias ca-root -file root.cer -keystore truststore.jks
这里有个坑:导入信任证书时,如果 truststore 不存在,keytool 会创建它。但默认密码是 changeit,很多人忘了改。
3.2.3 验证 SSL 连接(Java 视角)
# 用指定信任库连接服务
keytool -printcert -sslserver example.com:443 -J-Djavax.net.ssl.trustStore=truststore.jks
这个命令能模拟 Java 应用的行为。如果这里报错,你的 Java 应用大概率也会报错。
3.3 curl 与 wget:HTTP 层的证书验证
这两个工具是我们日常测试 HTTPS 最常用的。但很多人只用来下载文件,忽略了它们的证书验证能力。
3.3.1 curl 的证书验证
# 显示证书信息
curl -vI https://example.com
# 指定 CA 证书
curl --cacert ca-chain.pem https://example.com
# 忽略证书验证(仅测试用)
curl -k https://example.com
-v 输出里,你会看到:
* SSL connection using TLSv1.3 / AES256-GCM-SHA384* Server certificate:* subject: CN=example.com* start date: ...* expire date: ...* issuer: C=US; O=Let's Encrypt; CN=R3* SSL certificate verify ok.
警告: -k 或 --insecure 只能用于临时测试。生产环境绝对不要用。我见过有人把 -k 写进脚本里,结果证书过期了都没发现。
3.3.2 wget 的证书验证
# 显示证书信息
wget -d https://example.com
# 指定 CA 证书
wget --ca-certificate=ca-chain.pem https://example.com
# 忽略证书验证
wget --no-check-certificate https://example.com
wget 的 -d(debug)输出比 curl 更详细,但可读性差一些。我个人更习惯用 curl。
3.3.3 对比总结
| 特性 | curl | wget |
|---|---|---|
| 证书信息输出 | 简洁,易读 | 详细,但杂乱 |
| 指定 CA 证书 | --cacert |
--ca-certificate |
| 忽略验证 | -k |
--no-check-certificate |
| 支持协议 | 更多(HTTP/2, gRPC) | 较少 |
| 脚本友好度 | 高 | 中 |
说白了,日常调试用 curl,批量下载用 wget。
3.4 日志分析技巧:从报错到根因
工具只能帮你拿到数据,真正的功夫在于分析日志。证书相关的错误日志,其实模式很固定。
3.4.1 常见错误模式
| 日志关键字 | 可能原因 | 排查方向 |
|---|---|---|
certificate has expired |
证书过期 | 检查有效期,重新申请 |
unable to find valid certification path |
信任链不完整 | 检查中间证书是否导入 |
hostname in certificate didn't match |
域名不匹配 | 检查 SAN 和 CN |
self-signed certificate |
使用了自签名证书 | 导入 CA 证书到信任库 |
SSL handshake failed |
协议或密码套件不匹配 | 检查 TLS 版本和密码套件 |
3.4.2 我的日志分析流程
- 先看时间戳:错误是轮换后立即出现,还是过了一段时间才出现?这能区分是配置问题还是证书过期问题。
- 再看错误栈:Java 的
javax.net.ssl.SSLHandshakeException后面通常会跟一个 cause。这个 cause 才是根因。 - 最后看上下文:错误发生在哪个客户端?哪个服务端?是不是只有特定客户端报错?
我的经验: 有一次,Nginx 日志里全是 SSL: error:14094418,看起来像证书问题。但我用 openssl 测了没问题。最后发现是客户端时间不同步,差了 2 个小时,导致证书被认为「尚未生效」。所以,排查证书问题,别忘了检查系统时间。
3.4.3 日志分析命令
# 过滤证书相关错误
grep -i "certificate\|ssl\|handshake" /var/log/nginx/error.log
# 查看特定时间段的日志
sed -n '/2024-01-15 10:00/,/2024-01-15 11:00/p' /var/log/nginx/error.log | grep -i error
# 统计错误类型
awk '{for(i=1;i<=NF;i++) if($i ~ /error|failed/) print $i}' /var/log/nginx/error.log | sort | uniq -c | sort -rn
这些命令能帮你快速定位问题范围。
3.5 本章知识体系
下面这张图,总结了我们今天聊的所有工具和它们的使用场景。
嗯,工具就这些。但记住,工具只是手段。真正值钱的是你分析问题的思路。下次遇到证书报错,别慌。按这个流程走一遍,大部分问题都能定位到。