登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux OpenSSL s_client 检查证书链时如何读结果

来源:17golang原创

时间:2026-09-15 12:16:37 216浏览 收藏

Linux 上排查 HTTPS 证书,最容易误判的是“TLS 握手成功了,所以证书链没问题”。openssl s_client 默认更像诊断工具:它会把证书校验错误打印出来,但不一定因为错误立刻中止连接。真正可靠的读法是同时看 SNI、Certificate chain、每层的 subject/issuerVerify return code,最后再看命令退出状态。

官方文档:https://docs.openssl.org/3.5/man1/openssl-s_client/

要点速览
  • -servername 决定虚拟主机收到哪张证书,不能用裸 IP 连接结果代替域名检查。
  • -showcerts 展示服务器发来的证书列表;它不等于本机已经建立的完整信任链。
  • -verify_return_error 让校验错误返回失败;脚本还应检查退出码,不能只 grep 一行输出。

先用一条命令把证书链和主机名一起纳入检查

已知域名为 api.example.com、端口为 443 时,可以先使用下面的最小检查式。这里的输入只是文章示例,不代表该域名已被实际连接:

# 发送正确的 SNI,并要求证书校验失败时终止握手
openssl s_client \
  -connect api.example.com:443 \
  -servername api.example.com \
  -verify 5 \
  -verify_hostname api.example.com \
  -verify_return_error \
  -showcerts /null

-connect 指定 TCP 目标,-servername 把域名放进 TLS ClientHello。一个 IP 可能承载多个站点,缺少 SNI 时服务端可能返回默认站点的证书。-verify_hostname 将主机名匹配纳入验证;如果目标是 IP,应改用 -verify_ip,而不是把 IP 当作普通主机名。

-verify 5 开启服务器证书验证并设置链深度上限,-verify_return_error 让验证错误不再被忽略。重定向标准输入是为了避免命令进入交互模式;它不改变握手本身。

先看 Certificate chain,再看 Verify return code

输出中的 Certificate chain 通常按深度从 0 开始。深度 0 多数情况下是服务端叶子证书,后面的条目是服务端发送的中间证书;每行的 s: 是 subject,i: 是 issuer。判断链是否连贯时,重点检查上一张证书的 issuer 是否能对应下一层证书的 subject。

Certificate chain
  0 s:CN = api.example.com
   i:O = Example Intermediate CA
 1 s:O = Example Intermediate CA
   i:O = Example Root CA

Verify return code: 0 (ok)
Linux OpenSSL s_client Certificate chain、subject issuer 与 CA trust store 的静态关系示意图
图1:OpenSSL s_client 证书链静态结构示意图,展示 subject、issuer、服务器发送列表与本地信任库的关系。

上面的文字是结构示意,不是某次真实连接的运行证据。Verify return code: 0 (ok) 表示在当前验证参数和信任库条件下校验通过;它不能单独证明应用一定会连接成功,因为协议版本、ALPN、客户端证书等仍可能另有问题。反过来,看到证书列表也不能证明链已被信任:-showcerts 展示的是服务器发送的列表,根证书通常由本地信任库提供。

现象优先检查不要直接下的结论
depth=0,issuer 看起来正确但校验失败本机 CA 信任库、有效期、用途不是“服务端一定没发证书”
unable to get local issuer certificate中间证书是否由服务端发送、本机 CA 是否可用不是只重试命令
主机名验证失败叶子证书 SAN 与实际域名不是看 CN 就够了
链显示多张但仍不可信issuer/subject 连贯性与信任锚点不是“数量越多越完整”

把失败类型映射到信任库、链顺序和 SAN

排障时先把责任边界拆开。叶子证书的 Not BeforeNot After 负责时间有效性;SAN 负责目标域名或 IP 是否被覆盖;中间证书负责把叶子证书连接到受信任的根;本机 CA 文件或 CA 目录决定验证器能否找到可信锚点。

如果服务端返回了叶子证书,却提示找不到本地 issuer,先确认服务端是否按叶子到中间证书的顺序发送完整链,再确认 Linux 主机使用的 CA 来源。临时指定测试 CA 时可以使用 -verifyCAfile-verifyCApath;目录必须是 OpenSSL 认可的 hash 格式。不要把业务证书文件误当成信任库,也不要用关闭校验的参数来“修复”链问题。

如果连接到 IP 却想验证域名,结果可能与浏览器不同。把域名同时用于 -connect-servername-verify_hostname,才能把 DNS、SNI 和证书身份放在同一个检查条件里。生产脚本应记录错误文本、验证码和目标参数,便于判断是服务器配置还是检查机环境。

Linux OpenSSL s_client 的 SNI、SAN、issuer chain 与 verifyCAfile 静态边界示意图
图2:SNI、SAN 与信任链的静态边界示意图,说明主机名身份和 CA 信任不是同一个判断。

在自动化脚本里让失败真正返回非零

人工阅读输出时可以看最后一行,自动化则必须让校验错误影响退出状态。下面示例将输出保存下来,再依据命令是否成功决定后续动作:

# 保存诊断输出;命令失败时立即返回非零,避免误报成功
set -o pipefail
if openssl s_client \
  -connect api.example.com:443 \
  -servername api.example.com \
  -verify 5 \
  -verify_hostname api.example.com \
  -verify_return_error \
  -showcerts /null 2>tls-check.err | tee tls-check.out; then
  echo "证书链校验通过"
else
  rc=$?
  echo "证书链校验失败,退出码: $rc" >&2
  exit "$rc"
fi

这里的关键不是 grep "Verify return code: 0",而是使用严格验证参数并保留退出码。若只做信息采集而不想让握手因校验错误中止,可以去掉 -verify_return_error,但那种输出只能作为诊断线索,不能作为发布或切流门槛。

常见问题

为什么 s_client 显示 verify error,最后却还能连上?

诊断模式默认会继续显示更多证书问题;加入 -verify_return_error 才会把验证错误作为失败处理。

-showcerts 能证明服务器链配置正确吗?

不能。它只展示服务器发送的证书列表,仍需结合 issuer、信任库和 Verify return code 判断。

只检查证书过期时间够不够?

不够。还要检查 SAN、签发者链、用途以及当前 Linux 主机可用的信任锚点。

为什么同一个域名在浏览器正常,Linux 命令失败?

常见差异包括 SNI、代理、系统 CA 信任库和校验参数不同。先固定域名、SNI、主机名验证和 CA 来源,再比较结果。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>