证书没到期却报certificate has expired:OpenSSL定位中间证书与系统时间

常见场景:证书面板显示还有几个月才到期,客户端却报 certificate has expired。先拆开“证书没过期”:你看的是哪一张证书,又是哪台机器在判断时间?HTTPS 验证的是一条信任路径,不是只看到期日。

本文用离线测试证书复现中间证书过期和叶子尚未生效,不修改系统时钟也不装测试根;命令中的域名与 CA 文件均为示例,执行前替换成已授权目标与可信材料。

证书有效期排查:客户端时间、错误depth、验证链和重新验证

一、先确认谁在说“过期”

浏览器、curl、Java 和反向代理都可能充当 TLS 客户端:浏览器直连看本机时间,Nginx 回源看回源进程环境。服务端时钟正常,不能替另一台客户端作证。

先保存完整错误、目标域名、发生时间、客户端版本与部署位置。OpenSSL 的 depth=0 指目标叶子证书,depth=1 是它的签发者;继续往上是什么,要结合真实验证链看。

这里别急着重新签发。只换叶子、却继续搭配过期的中间证书,有点像换了新车票,仍拿着过期证件进站。

二、核对客户端时间,而不只是时区

date -u
openssl version
openssl x509 -in leaf.pem -noout -subject -issuer -dates

notBefore 是生效时间,notAfter 是到期时间。当前时间晚于后者可能报过期,早于前者可能报 certificate is not yet valid。两者不是同一个故障,不能一律归因于续期失败。

显示为 CST 还是 UTC 不会凭空让证书失效,要核对的是实际时间点。检查宿主机、虚拟机的时钟同步;容器共享宿主机内核时钟,重启容器也不会自动校准时间。

时钟确实异常就按环境的时间同步规范修复,别把生产机器日期往回拨——数据库、日志和鉴权都不喜欢这种“时间旅行”。

三、从线上拿证据,不只看磁盘文件

openssl s_client -connect api.example.com:443 \
  -servername api.example.com -verify_hostname api.example.com \
  -showcerts -verify_return_error </dev/null

-servername 发送 SNI,-verify_hostname 指定验证名称;用途不同,别省成一个。-verify_return_error 让验证错误终止握手,避免仅凭“连上了”误判成功。企业私有 CA 场景应额外指定经过核验的 -CAfile

-showcerts 展示服务端发来的证书列表,并不等于客户端最终构建、验证通过的链。保存输出中的证书块及错误行,分别查看 subject、issuer、日期和指纹,不要直接把输出中的证书导入信任库。

服务端发送列表通常不含根证书;客户端还可能用本地信任库构建另一条路径,所以“服务器发了三张”不等于“验证恰好用了这三张”。有 CDN 或多节点时,在相同 SNI 下分别确认命中端点。

四、把链分开,定位具体哪一张过期

准备三个来源明确的文件:leaf.pem 只放目标叶子证书,intermediates.pem 放需要的中间证书,trusted-roots.pem 放从可信渠道取得的根证书——它不是从故障网站随手下载就能信任的东西。

openssl verify -show_chain -purpose sslserver \
  -verify_hostname api.example.com \
  -CAfile trusted-roots.pem \
  -untrusted intermediates.pem leaf.pem

-untrusted 在这里表示“可以帮助建链、但不是信任锚”,不表示中间证书一定有问题。成功看 OK 和退出码;失败把错误码、depth 与证书主体一起记录,再对应到单张文件。

看到的现象优先检查不要直接下的结论
error 10,depth 0叶子到期日及验证时间一定是中间证书问题
error 10,depth 1路径中叶子的签发者网站叶子证书过期
error 9,depth 0叶子notBefore与客户端时间只需补全证书链
unable to get local issuer certificate签发链材料与信任库与过期错误完全相同

五、离线实验:叶子有效,链仍失败

本次实测用 OpenSSL 1.1.1k 构造同一测试中间 CA 公钥的两张证书:一张有效、一张已过期,叶子仍在有效期内,签名关系不变。有效中间证书参与验证时返回 OK,换成过期版本后,真实错误为 error 10 at 1 depth lookup: certificate has expired

另一个样本把叶子生效日设在未来,得到 error 9 at 0 depth lookup: certificate is not yet valid。两个样本执行 x509 -checkend 0 时都返回 0:它只回答到期检查,不验证 notBefore、主机名或整条链。

实验还确认,OpenSSL 会检查本次路径中根 CA 证书的有效期;不能把这条实现行为泛化成所有浏览器、所有 TLS 库都完全一致。测试私钥只在内存中生成,证书临时文件已清理;这些是可控样本,不是生产故障统计。

六、用指定时间复现,不改机器日期

# incident_epoch 是已核对的故障时刻 Unix 秒数
openssl verify -attime "$incident_epoch" \
  -purpose sslserver -verify_hostname api.example.com \
  -CAfile trusted-roots.pem \
  -untrusted intermediates.pem leaf.pem

-attime 只改变这次验证使用的参考时刻,不改操作系统时间。本次实验把参考时刻移到中间证书到期前、且叶子已生效的时间段,同一组文件恢复 OK。这能解释“为什么昨天可以,今天不行”。

但它不是生产修复开关,也不是完整历史重放:今天的信任库未必等于故障当时,吊销状态、路径选择与客户端版本都可能变化。留存故障时的公开证书、错误日志与信任库版本,结论才有边界。

七、修复应跟着错误对象走

叶子到期就更新叶子并核验线上指纹;中间证书过期,从 CA 官方取得与当前叶子匹配的可用链,验证后按部署流程更新。名字不能当依据,公钥与签名关系也必须匹配。

只有旧设备失败时,比较它的信任库、TLS 库版本与实际建链结果;多链场景按目标客户端兼容性选择,并让旧客户端真实验证,别在新电脑上点开网页就宣布收工。

别用 curl 的 -k、关闭验证或跳过时间检查当修复,它们只是让报警器不响。改部署配置前先备份、做配置测试,再按既有变更流程执行,本文不要求直接 reload 任何生产服务。

八、验收清单:查日期,也查实际路径

  • 错误来自哪个客户端、哪个端点,已明确。
  • 客户端真实时间可信,notBefore与notAfter均核对。
  • 错误depth对应证书已定位,不只看叶子日期。
  • 信任根来源可靠,中间证书未误作信任锚。
  • 域名、用途、链与时间验证保持开启,命令返回成功。
  • 受影响客户端已复测,多节点没有遗漏,证据脱敏留存。

监控建议分成两项:单张证书到期预警,加上真实客户端的完整握手验证。前者提醒何时换证,后者发现链与环境问题,别让一个绿色到期数字替整条链路作保。

官方参考:OpenSSL验证选项与建链规则s_client参数说明x509日期与checkend说明。文档以3.0系列为参考,核心验证行为另以本机1.1.1k实测,实际客户端仍须按自身版本验收。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lsetea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值