证书能被OpenSSL读出来,域名和有效期也像是对的,执行verify却报certificate signature failure。这时别急着补SAN,也别把证书塞进信任库碰运气。error 7关注的是证书签名验证,不是"文件长得像证书"就能过关。本文按错误深度、签发者材料、文件一致性和复验四步排查。
一、error 7和"找不到签发者"不是一回事
X.509证书包含待签名内容、签名算法和签名值。验证者使用所选签发者的公钥检查签名,以确认这些内容与签名是否对应。OpenSSL把certificate signature failure列为证书签名验证失败;命令行常见数字为7,不要把这个错误号当成进程退出码。
error 20通常是构链缺签发者,error 62是主机名不匹配。RSA或ASN.1错误栈也要完整保存。解析、构链、验签、用途校验是不同关卡。

二、先确认到底是哪张证书失败
把收到的叶子证书单独保存为leaf.pem,签发链中间证书保存为intermediate.pem,事先可信的根证书保存为root.pem。示例使用保留测试域名api.example.test,生产排查时替换成实际业务名称,不能照抄测试名得出业务结论。
bash
openssl version
openssl x509 -in leaf.pem -noout -subject -issuer -serial -dates
openssl x509 -in leaf.pem -noout -text
depth=0指叶子证书,depth=1通常是它上一级的中间CA,继续向上递增。先看错误对应的subject,再结合链文件定位。若中间证书签名坏了,重签叶子不一定解决问题。
注意x509通常只处理输入中的首张证书。对fullchain执行一次x509成功,不能证明后续每张证书都正确,更不能证明签名有效。这里查看的是字段和编码,不是签名验收报告。
三、用受控信任材料重跑完整验证
bash
openssl verify -no-CApath -CAfile root.pem \
-untrusted intermediate.pem -purpose sslserver \
-verify_hostname api.example.test -show_chain leaf.pem
-CAfile提供本次明确选定的信任材料,-no-CApath避免默认目录悄悄补链;-untrusted提供构链候选,不会因为放进去就变成信任锚。示例仍明确验证服务端用途和业务名称,避免只修好一处后把其他失败藏起来。
不能拿网上随手下载的根证书来"修复"信任。应从CA官方分发渠道或受控交付记录取得材料,核对来源与指纹。issuer和候选CA的subject相同只是线索,不代表两者公钥一定对应;AKI/SKI也属于查找签发者的线索,不替代密码学验签。
同名不同密钥的CA,可能被AKI/SKI排除,也可能走到验签失败;"同名CA一定报7"不是规律。若报20,先修构链问题。
四、比较原始交付件,而不是凭肉眼看PEM
从可信交付记录重新取得同一张证书,命名为original.pem。下面只转换和比较公开证书,不涉及私钥;输出写在专用排查目录,避免覆盖已有文件。每条转换命令都应先确认成功,再执行比较。
bash
openssl x509 -in leaf.pem -outform DER -out received.der
openssl x509 -in original.pem -outform DER -out original.der
sha256sum received.der original.der
cmp received.der original.der
PEM换行不同,文件哈希可能不同,DER却可以相同;先分清是"装箱纸"还是证书本体不同。cmp退出0表示文件一致,1表示不同,其他非零值应按读取错误等情况处理。哈希相同只证明两份材料一致,不证明它本来可信。
如果DER不同,先核对是否确为同一次签发、同一序列号和同一条链,不要立刻宣布遭到篡改。重新签发、交叉签名、拿错版本都可能产生不同证书。若确认传输或复制损坏,从可信来源重新部署;若源材料本身有问题,交由签发方核查,不要手改Base64或签名字段。
五、隔离实验:字段可读不等于签名可验
本次在临时目录使用OpenSSL 1.1.1k FIPS生成测试根、中间CA和叶子证书,未连接生产服务。基线链验证成功;只改变叶子证书签名值中的一个字节、保持DER结构可解析后,x509仍能显示subject和有效期,但verify在depth=0报certificate signature failure。
另一个负例只改变中间CA的签名值,叶子内容不变,失败转到depth=1。恢复正确中间证书后重新通过。这些结果说明应定位失败对象,不能据此推断所有error 7都来自传输损坏。实验还单独覆盖了缺中间证书与错误主机名,分别得到20与62,没有混为一个原因。
受信任自签根还有一个边界:OpenSSL默认不检查其自签名,-check_ss_sig可要求检查。信任锚可信来自外部信任决策,不是"能验证自己的签名";该选项也不是给陌生根证书建立信任的按钮。
六、按现象选择下一步
| 观察 | 优先检查 | 不要这样做 |
|---|---|---|
| 7,depth=0 | 叶子原件、所选签发者公钥 | 只补SAN或改有效期 |
| 7,depth=1 | 中间CA原件及其上级材料 | 只替换叶子证书 |
| 20,无法取得签发者 | 缺失链、候选与信任锚 | 直接断言签名损坏 |
| 62,hostname mismatch | 业务名称与证书标识 | 当成error 7修复 |
| PEM解析失败 | 格式、边界与输入文件 | 跳过解析直接谈验签 |
七、把退出状态带回自动化流程
bash
if openssl verify -no-CApath -CAfile root.pem \
-untrusted intermediate.pem -purpose sslserver \
-verify_hostname api.example.test leaf.pem >verify.log 2>&1; then
printf 'offline verification passed\n'
else
rc=$?
printf 'verification failed: rc=%s; see verify.log\n' "$rc" >&2
exit "$rc"
fi
这段Bash把输出写入独立日志,并把失败退出码传给调用者。重定向本身失败也应阻止流程继续;此时应查日志目录权限和磁盘,而不是把它当成证书验签错误。不要用关闭验证来把红灯涂绿。
本文实验验证的是本机OpenSSL离线链,不代表浏览器、Java或Nginx加载已经验收。实际库版本、算法策略和信任库可能不同。若错误来自TLS握手,还要分别检查证书链签名与握手签名算法,不能把no suitable signature algorithm直接归为error 7。
八、修复后的验收清单
- 留存完整错误、版本、depth与材料来源,不公开私钥、密码或内部业务信息。
- 对照可信原件,确认叶子和中间链身份,完整验证退出0且用途与名称匹配。
- 按变更流程重新部署,在真实终点、真实SNI及目标客户端信任库下验证TLS;离线通过只是其中一步。
- 保留修复前后证书指纹与日志,确认没有靠扩大信任范围或关闭验证绕过问题。
参考:OpenSSL verify文档、OpenSSL x509文档、RFC 5280:证书结构与路径验证。新环境按对应版本文档复核。