链不完整是什么问题
服务端只下发叶证书、没有随附中间证书时,客户端无法把叶证书接回受信任根,于是报出 unable to get local issuer certificate(校验错误码 20)。浏览器有时会凭缓存或补链能力掩盖问题,但 Java、小程序、命令行与部分移动端库会直接失败。修复方式是把中间证书按顺序拼到叶证书之后一并下发。
基本信息
| 典型报错 | unable to get local issuer certificate (20)、unable to verify the first certificate |
|---|---|
| 根因 | 服务端未下发中间证书,或链顺序颠倒 |
| 正确顺序 | 叶证书在前,中间证书在后;根证书通常无需下发 |
| 验证命令 | openssl verify -untrusted chain.pem leaf.pem |
| 线上验证 | openssl s_client -showcerts 数 BEGIN CERTIFICATE 的条数 |
| 常见诱因 | 证书商给的多个文件只配了其中一个、自动续期脚本改写配置 |
典型使用场景
- 新证书上线:证书商常提供多个文件,需确认拼成完整链
- 自动续期后故障:续期脚本只替换叶证书,链文件被截断
- 多环境差异:测试环境用自签链正常,生产未配中间证书
- 客户端差异排查:浏览器正常但 App 报错时优先怀疑链
- 负载均衡与 CDN:边缘节点证书链与源站不一致时的对比定位
拼接与验证
拼接与验证
# 拼接完整链:叶证书在前,中间证书在后
cat server.crt intermediate.crt > fullchain.pem
# 离线验证:-untrusted 提供中间证书,根证书由系统信任库提供
openssl verify -untrusted intermediate.crt fullchain.pem
# 查看文件里包含几张证书,正常情况下应为 2 张或更多
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
# 抓取线上实际下发的证书,数条数是否符合预期
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'服务端链配置示例
服务端链配置示例
ssl_certificate /etc/nginx/ssl/example.com.fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# nginx 1.11.0 起也可用多条 ssl_certificate 按顺序指定链,但拼成单文件更直观报错与处置
| 现象 | 原因 | 处置 |
|---|---|---|
| error 20 | 中间证书缺失 | 把中间证书拼接到叶证书之后重新加载 |
| error 21 | 链顺序颠倒或含无关证书 | 按叶证书在前的顺序重排 |
| error 19 | 自签证书不在信任库 | 导入企业 CA 或改用公共 CA 签发 |
| 浏览器正常但 App 失败 | 浏览器有补链或缓存机制 | 以 s_client 与 App 实测为准,修好链 |
| 续期后突然报错 | 续期脚本只替换了叶证书 | 检查续期脚本是否同步更新链文件 |
把根证书一并下发既增加握手体积也没有收益:受信任根已内置在客户端信任库里。真正需要补的是中间证书,而且顺序必须是叶证书在前。跨签或链较长的证书体系可能包含两级中间证书,需要全部按序拼接。
只在浏览器里验证通过就上线是常见风险:浏览器会尝试下载缺失的中间证书或使用缓存,掩盖链不完整问题。上线前用 s_client 数证书张数、并用一个严格校验的客户端(如 curl 不带 -k 或 Java 程序)实测。