OpenSSL客户端证书链验证异常排查及正确验证方法
问题场景
我持有三张构成有效信任链的证书:
- 根CA(Root CA,自签名)
- 中级CA(Intermediate CA,由根CA签发)
- 客户端证书(Client Certificate,由中级CA签发)
需要验证客户端证书确实由中级CA/根CA签发,但使用常规命令时遇到了验证不完整的问题。
错误尝试与问题分析
- 最初使用网上找到的命令:
openssl verify -CAfile root-ca.pem -untrusted Intermediate.pem ClientCert.pem
返回ClientCert.pem: OK,但实际验证逻辑不完整——把root-ca.pem换成无关的根CA文件,结果依然返回OK。
原因是OpenSSL的verify命令有个默认规则:如果找到自签名证书,会默认将其视为根CA。这里中级CA虽不是自签名,但实际验证时,OpenSSL错误地将中级CA当作了信任锚,直接跳过了对根CA的验证,导致指定的root-ca.pem根本没被用到。
- 移除
-untrusted参数直接验证:
openssl verify -CAfile root-ca.pem ClientCert.pem
返回错误:
error 20 at 0 depth lookup: unable to get local issuer certificate error ClientCert.pem: verification failed
这是因为OpenSSL找不到客户端证书的直接签发者(中级CA),无法构建完整的信任链。
尝试把客户端证书和中级CA打包到同一个文件,结果OpenSSL只读取文件中的第一个证书,还是无法完成验证。
额外发现:用无关根CA验证中级CA时,命令
openssl verify -CAfile some-random-ca.pem Intermediate.pem居然返回OK,这也说明-CAfile参数在这种场景下没有起到预期的信任锚作用。
正确解决方案:使用-trusted指定信任锚
经过测试,使用-trusted参数指定根CA作为唯一信任锚,配合-untrusted传入中级CA证书,就能完成完整的链验证:
openssl verify -trusted root-ca.pem -untrusted Intermediate.pem ClientCert.pem
-trusted参数说明
-trusted file
指定包含信任锚证书的文件,除非指定-partial_chain选项,否则这些证书必须是自签名的。文件可包含一个或多个PEM格式的证书。使用该选项后,不会再参考其他默认证书列表,也就是说只有该文件中的证书会被当作信任锚。该选项可多次指定以加载多个文件中的信任证书,同时会隐含-no-CAfile和-no-CApath选项,不能与-CAfile或-CApath同时使用。
当指定错误的根CA时,OpenSSL会正确返回验证错误:
error 20 at 1 depth lookup: unable to get local issuer certificate
疑问解答
- 仅用根CA和客户端证书无法完成验证——因为客户端证书的直接签发者是中级CA,OpenSSL需要这个中间证书来连接客户端证书与根CA,否则找不到两者之间的信任关联。
- 可以验证完整的信任链,但必须用正确的参数组合:用
-trusted指定根CA作为唯一信任锚,用-untrusted提供中级CA证书,这样OpenSSL会先验证中级CA是否由根CA签发,再验证客户端证书是否由中级CA签发,完成完整的链验证。
内容的提问来源于stack exchange,提问作者CJW

