# error 20：unable to get local issuer certificate 的修法

> OpenSSL 错误 20 表示在信任库和提供的中间证书里都找不到某张证书的签发者。先看 depth，再补中间证书或正确的根证书。

Source: https://ssl.sh/zh/errors/openssl-verify-error-20-unable-to-get-local-issuer/
Last updated: 2026-11-10

**错误 20 的意思是：OpenSSL 找不到证书链中某张证书的签发者证书。** 报错里的 depth 说明断在哪一环：depth 0 表示没提供站点证书的签发者（中间证书）；depth 更大表示链条终止在一个不在你信任库里的 CA。

## 快速修复

depth 0 报错就补中间证书，depth 1 及以上报错就把正确的根证书加进信任库。先测试服务器，看 depth 和它发出的证书链：

```bash
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>&1 \
  | grep -E "depth=|verify error|^ *[0-9] s:|Verify return"
```

验证本地文件时，显式提供中间证书和根证书：

```bash
openssl verify -CAfile root.pem -untrusted intermediates.pem leaf.pem
```

- **s_client 只显示一张证书，depth 0 报错误 20：** 服务器必须发送中间证书，见[缺少中间证书](https://ssl.sh/zh/errors/missing-intermediate-certificate/)。
- **depth 1 或 2 报错误 20：** 链顶那张证书不在你的信任库里，加入正确的根证书或更新 CA 证书包。

## 原因

最常见的是服务器没提供中间证书（depth 0），其次是根证书不在信任库里。

### 1. 没提供中间证书（depth 0）

- **确认：** s_client 输出里只有一行 `s:`；或者本地验证时 `openssl verify leaf.pem` 失败，而 `openssl verify -untrusted intermediates.pem leaf.pem` 成功。
- **修复：** 服务器上部署“站点证书 + 中间证书”（`fullchain.pem`）。阿里云、腾讯云下载的 Nginx 格式证书通常已经包含中间证书，宝塔粘贴时要粘贴完整内容。本地验证文件时始终用 `-untrusted` 传入证书链。[证书链补全工具](https://ssl.sh/zh/tools/chain-builder/)能为站点证书找到正确的中间证书。

### 2. 根证书不受信任（depth ≥ 1）

证书链是完整的，但终止于你的信任库里没有的 CA：私有 CA、企业代理，或者老系统上的较新公网根证书。

- **确认：** `-showcerts` 最后一行 `i:` 显示的 CA 不在 `/etc/ssl/certs/`（或你使用的证书包）里。
- **修复：** 把私有根证书加入系统信任库，测试时用 `-CAfile` 指定。公网根证书缺失就更新 `ca-certificates`。

### 3. 用错了信任库

自定义的 `-CAfile` 或 `SSL_CERT_FILE` 会替换默认信任锚。

- **确认：** 去掉 `-CAfile` 或环境变量后，同样的命令就成功了。
- **修复：** 使用同时包含所需公网根证书和私有根证书的证书包。

### 4. 验证了错误的文件

验证的是中间证书或 CA 证书包，而不是站点证书；或者站点证书和中间证书不是同一条链，也会报错误 20。

- **确认：** `openssl x509 -in file.pem -noout -subject -issuer` 显示的不是你的服务器证书。
- **修复：** 把证书包拆开，验证站点证书。

## 各环境修法

Linux 上把私有根证书放进系统信任库再刷新；macOS 和 Windows 上要先确认 OpenSSL 实际读取的是哪个 CA 文件。

**Debian / Ubuntu：** 私有根证书放到 `/usr/local/share/ca-certificates/*.crt`，执行 `sudo update-ca-certificates`。

**CentOS / RHEL / Rocky / Alibaba Cloud Linux：** 放到 `/etc/pki/ca-trust/source/anchors/`，执行 `sudo update-ca-trust`。

**macOS：** 系统自带的 LibreSSL `openssl` 和 Homebrew 的 OpenSSL 用的是不同的 CA 文件，用 `openssl version -d` 查看各自的目录。

**Windows：** Windows 版 OpenSSL 不读 Windows 证书库，用 `-CAfile` 指定从证书库导出的 PEM 证书包，或从 curl.se 下载的证书包。

**其他工具里的同一个错误：** curl 报错 60、Python 的 `CERTIFICATE_VERIFY_FAILED`、Node.js 的 `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`，底层都是 OpenSSL 的错误 20，见 [curl 报错 60](https://ssl.sh/zh/errors/curl-60-ssl-certificate-problem/)。

## 不要这样做

这些绕过办法只会掩盖证书链问题。

- **因为 s_client 还能连上就忽略错误 20。** s_client 默认在验证出错后继续连接，真实客户端不会。加 `-verify_return_error` 让它像真实客户端一样失败。
- **把中间证书当作受信任根证书加入信任库来消除报错。** 信任锚应该是根证书；这样做只是让你自己看不到服务器证书链的问题，你的用户照样会报错。
- **把站点证书放进信任库。** 能“解决”到下次续期，然后又坏了。
