# OpenSSL 常见报错排查：含义、诊断命令与修复

> 按报错索引的 OpenSSL 3.x 排查手册：找不到签发者、证书过期、主机名不匹配、wrong version number、握手失败、私钥不匹配、PKCS#12 打不开等。

Source: https://ssl.sh/zh/cookbook/troubleshooting/
Last updated: 2026-11-10

下面每条常见 OpenSSL 报错都附有定位问题的命令和修复办法。报错原文按 OpenSSL 3.6.1 的输出引用，1.1.1 或 3.0 写法不同时一并列出。如果出问题的是公网网站，可以先用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)看一眼；想精确看到 OpenSSL 眼中的情况，再用下面的命令。

## 解码任意 OpenSSL 错误码

`openssl errstr` 把报错行里的十六进制错误码翻译成原因。

```bash
openssl errstr 0A000086 0A000410 0308010C
```

```text
error:0A000086:SSL routines::certificate verify failed
error:0A000410:SSL routines::ssl/tls alert handshake failure
error:0308010C:digital envelope routines::unsupported
```

完整的一行长这样：`80FF14F601000000:error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2126:`。第一段是线程号，后面依次是错误码、库、函数和原因。遇到 `certificate verify failed` 时，真正的原因在它上面那行 `verify error:num=`。

## 解决 unable to get local issuer certificate（错误 20）

客户端找不到给服务器证书签名的那张证书：多数是服务器漏发了中间证书，少数是客户端缺根证书。

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

```text
verify error:num=20:unable to get local issuer certificate
verify error:num=21:unable to verify the first certificate
 0 s:CN=www.example.com
Verify return code: 21 (unable to verify the first certificate)
```

证书链里只有一张证书，说明服务器缺中间证书：Certbot 要配 `fullchain.pem`，商业证书要把服务器证书和 CA 证书包拼在一起；宝塔面板里“证书（PEM 格式）”一栏要粘贴完整证书链。证书链完整却仍然报错，就是客户端信任库缺根证书：更新 `ca-certificates` 或用 `-CAfile` 指定。和 [curl 错误 60](https://ssl.sh/zh/errors/curl-60-ssl-certificate-problem/)、[缺少中间证书](https://ssl.sh/zh/errors/missing-intermediate-certificate/)是同一个根因。

## 解决 certificate has expired（错误 10）与 not yet valid（错误 9）

拿证书的有效期和执行检查的那台机器的时钟比一比。

```bash
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts < /dev/null 2>/dev/null \
  | grep -E ' [0-9] s:|v:NotBefore'
date -u
```

```text
 0 s:CN=www.example.com
   v:NotBefore: Nov  3 13:44:34 2026 GMT; NotAfter: Feb  1 13:44:33 2027 GMT
 1 s:C=CN, O=Example Corp, CN=Example Intermediate CA
   v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
Tue Nov 10 02:30:00 UTC 2026
```

服务器证书过期就续期并重载服务：磁盘上的文件换了，Nginx 或 Apache 不 reload 是不会生效的，宝塔面板续签后也要确认 Nginx 已重载。链上的中间证书过期，就换成 CA 当前提供的证书包。证书明明有效却报错 9 或过期，多半是本机时钟不对，常见于没有配 NTP 的嵌入式设备和内网机器。浏览器里对应的是 [NET::ERR_CERT_DATE_INVALID](https://ssl.sh/zh/errors/net-err-cert-date-invalid/)。

## 解决 hostname mismatch（错误 62）

你访问的域名不在证书的 SAN（使用者备用名称）里。

```bash
openssl s_client -connect www.example.com:443 -servername shop.example.com \
  -verify_hostname shop.example.com < /dev/null 2>/dev/null | grep -E 'verify error|Verify return'
openssl x509 -in cert.pem -noout -ext subjectAltName
```

```text
verify error:num=62:hostname mismatch
Verify return code: 62 (hostname mismatch)
X509v3 Subject Alternative Name: 
    DNS:www.example.com, DNS:example.com
```

要么重新签发把缺的域名加进 SAN，要么让客户端改用证书里有的域名。通配符只匹配一级子域名。如果返回的证书属于另一个网站，说明服务器因为没收到 SNI，或者这个域名没有对应的站点配置，返回了默认证书；宝塔面板里没绑定该域名的站点就会出现这种情况。浏览器里对应的是 [NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/zh/errors/net-err-cert-common-name-invalid/)。

## 解决 self-signed certificate in certificate chain（错误 19）

证书链的顶端是客户端不信任的根证书，通常是私有 CA，或者做 TLS 解密的代理、安全软件。

```bash
openssl s_client -connect www.example.com:443 -servername www.example.com < /dev/null 2>&1 \
  | grep -E '^depth|verify error' | head -4
```

```text
depth=2 C=CN, O=Example Corp, CN=Example Root CA
verify error:num=19:self-signed certificate in certificate chain
depth=1 C=CN, O=Example Corp, CN=Example Intermediate CA
depth=0 CN=www.example.com
```

顶端如果是你们公司自己的 CA，就把它加进客户端信任库（见[搭建私有 CA](https://ssl.sh/zh/cookbook/private-ca/)），不要关掉证书校验。如果是陌生的企业或杀毒软件 CA，说明流量被中间设备解密了。错误 18（`self-signed certificate`）表示服务器证书本身就是自签名的。OpenSSL 1.1.1 中这两个错误写作不带连字符的 `self signed`。更多见 [self-signed certificate in certificate chain](https://ssl.sh/zh/errors/self-signed-certificate-in-certificate-chain/)。

## 解决 wrong version number 与 packet length too long

这两个报错说明对方根本没用 TLS 回应，最常见的是连到了明文 HTTP 端口。

```bash
openssl s_client -connect www.example.com:80 < /dev/null
curl -sI http://www.example.com:443/ | head -1
```

```text
...:error:0A0000C6:SSL routines:tls_get_more_records:packet length too long:...
...:error:0A000139:SSL routines::record layer failure:...
HTTP/1.1 200 OK
```

OpenSSL 1.1.1、3.0 一般报 `wrong version number`；较新的版本（这里实测 3.6）遇到同样情况可能报 `packet length too long`。curl 那一行说明 443 端口在用明文 HTTP 应答。检查 Nginx 的 `listen 443 ssl` 是否漏了 `ssl`、客户端配置的端口，以及代理是否要求先发 `CONNECT`。邮件客户端在 STARTTLS 端口上直接用 SSL（587 与 465 搞反）也会这样。

## 解决 handshake failure（告警 40）与 no shared cipher

客户端和服务器找不到共同支持的协议版本、密码套件、密钥交换组或签名算法。

```bash
openssl s_client -connect www.example.com:443 -servername www.example.com < /dev/null 2>&1 | grep -m1 alert
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2 -cipher 'ALL:@SECLEVEL=0' -brief < /dev/null
```

```text
...:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:...:SSL alert number 40
```

服务器日志里对应的是 `no shared cipher`。典型原因有：套件列表把客户端支持的全排除了；只部署了 ECDSA 证书而客户端只接受 RSA；`ssl_ecdh_curve`/groups 设置过窄；或者 TLS 1.2 服务器强制要求客户端证书。1.1.1 等老版本对同一告警显示 `sslv3 alert handshake failure`。浏览器里显示为 [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://ssl.sh/zh/errors/err-ssl-version-or-cipher-mismatch/)，对比双方列表的方法见[密码套件与协议](https://ssl.sh/zh/cookbook/ciphers-and-protocols/)。

## 解决 no protocols available 与 alert protocol version（告警 70）

`no protocols available` 是你自己的客户端拒绝了，`alert protocol version` 是服务器拒绝了你提供的版本。

```bash
openssl s_client -connect www.example.com:443 -tls1_1 < /dev/null
openssl s_client -connect www.example.com:443 -tls1_1 -cipher 'DEFAULT:@SECLEVEL=0' < /dev/null
```

```text
...:error:0A0000BF:SSL routines:tls_setup_handshake:no protocols available:...
...:error:0A00042E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version:...:SSL alert number 70
```

OpenSSL 3.x 在默认安全级别下禁用 TLS 1.0/1.1，所以基于它的客户端连不上只支持旧协议的老服务器，要等服务器升级到 TLS 1.2。内网里还有老设备必须继续用的话，应尽快升级设备，降低 `@SECLEVEL` 只能临时救急。浏览器里显示为 [ERR_SSL_PROTOCOL_ERROR](https://ssl.sh/zh/errors/err-ssl-protocol-error/) 或版本不匹配。

## 解决 key values mismatch（证书与私钥不匹配）

证书和私钥不是一对，或者证书链文件把中间证书放在了第一位。

```bash
openssl x509 -in fullchain.pem -noout -subject
openssl x509 -in fullchain.pem -noout -pubkey | openssl sha256
openssl pkey -in privkey.pem -pubout | openssl sha256
```

```text
subject=C=CN, O=Example Corp, CN=Example Intermediate CA
SHA2-256(stdin)= d6f7b12a13ec99a751aad52deee4ce9339710d60fb07acefa86eb1e8d753648d
SHA2-256(stdin)= 3152719d149366da47e37904e2740fb6d296ade304092ce5308560b45d75e458
```

这里文件里的第一张是中间证书，Nginx 拿错了公钥去比：把服务器证书放到最前面（`cat leaf.pem intermediate.pem > fullchain.pem`）。如果第一张确实是自己的域名但哈希不同，说明私钥属于别的证书，常见于重新生成过 CSR、或者在云控制台下载证书时混用了旧私钥；找到对应私钥或重新签发。Nginx 里的报错就是 [key values mismatch](https://ssl.sh/zh/errors/nginx-key-values-mismatch/)。

## 解决读取 PKCS#12 时的 unsupported

OpenSSL 3.x 把 RC2 等老算法移到了 legacy provider，读取老的 `.pfx`/`.p12` 要加 `-legacy`。

```bash
openssl pkcs12 -in old.pfx -info -noout
openssl pkcs12 -in old.pfx -info -noout -legacy
```

```text
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 16
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Error outputting keys and certificates
...:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:...:Algorithm (RC2-40-CBC : 0), Properties ()
...
Certificate bag
Certificate bag
```

加上 `-legacy` 的第二条就能正常读取。想把文件升级成新格式，先用 `-legacy` 解出来，再不带 `-legacy` 重新导出，得到的就是 OpenSSL 3.0 起默认的 AES-256-CBC + PBKDF2。只有老软件读不了新格式时，才反过来用 `-export -legacy`。早年从 Windows 导出、或由老版本工具生成的 PFX 常需要这样处理，详见 [PKCS#12 命令](https://ssl.sh/zh/cookbook/pkcs12/)。

## 解决 Mac verify error: invalid password?

PKCS#12 密码错了，或者文件密码是空的而你没给（反过来也一样）。

```bash
openssl pkcs12 -in bundle.p12 -noout -passin pass:wrong
openssl pkcs12 -in bundle.p12 -noout -passin pass:
```

```text
Mac verify error: invalid password?
```

第二种写法显式传入空密码，Windows 不设密码导出的文件、用空密码转换的 Java 密钥库就是这种情况。云厂商下载的 IIS/Tomcat 格式证书包里一般附带一个密码文本文件，别和别的证书包搞混。在多人共用的机器上不要把真实密码写在命令行里，用 `-passin file:pass.txt` 或 `env:变量名`。

## 解决加载私钥时的 bad decrypt

加密私钥的口令不对。

```bash
openssl pkey -in privkey.pem -noout
head -2 privkey.pem
```

```text
Could not find private key of key from privkey.pem
...:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:...
...:error:11800074:PKCS12 routines:PKCS12_pbe_crypt_ex:pkcs12 cipherfinal error:...:maybe wrong password
-----BEGIN ENCRYPTED PRIVATE KEY-----
```

看到 `ENCRYPTED PRIVATE KEY`（PKCS#8）或者 `Proc-Type: 4,ENCRYPTED` 头（传统格式），就说明私钥是加密的。Web 服务器启动时没法交互输入口令，确认口令无误后，导出一份不加密的副本并收紧权限：`openssl pkey -in privkey.pem -out privkey-plain.pem && chmod 600 privkey-plain.pem`。

## 解决 Could not find certificate 与 no start line

OpenSSL 没找到预期类型的 PEM 块：文件拿错了、其实是 DER，或者内容被破坏。

```bash
file cert.pem
head -1 cert.pem
openssl x509 -in cert.pem -noout -subject
openssl asn1parse -in cert.pem | head -3
```

```text
cert.pem: ASCII text
-----BEGIN CERTIFICATE-----
Could not find certificate from cert.pem
...:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:...:No supported data to decode. Input structure: Certificate
...:error:0680009B:asn1 encoding routines:ASN1_get_object:too long:...
```

OpenSSL 1.1.1 对应的报错是 `unable to load certificate ... no start line`。`asn1parse` 报 `too long` 或 `header too long`，说明 Base64 正文被截断或改动过，通常是复制粘贴时少了一行，重新获取文件即可。`file` 显示 `data` 或 `Certificate` 时是 DER 格式，加 `-inform DER`。OpenSSL 3.6 本身能容忍 UTF-8 BOM 和 Windows 换行（CRLF），但 Java、Go 和一些控制面板不行：在 Windows 记事本里编辑过的证书，用 `dos2unix cert.pem` 或 `sed -i '1s/^\xEF\xBB\xBF//' cert.pem` 处理一下。把私钥当证书传进去也是同样的报错，所以先看第一行。格式转换见[证书格式转换](https://ssl.sh/zh/cookbook/convert-formats/)。

## 解决 unknown ca（告警 48）与 certificate required（告警 116）

在双向 TLS 中，这两个告警来自服务器：它拒绝了客户端证书，或者根本没收到。

```bash
openssl s_client -connect api.example.com:443 -servername api.example.com -brief < /dev/null 2>&1 | grep alert
openssl s_client -connect api.example.com:443 -servername api.example.com \
  -cert client.crt -key client.key -brief < /dev/null 2>&1 | grep alert
```

```text
...:error:0A00045C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required:...:SSL alert number 116
...:error:0A000418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:...:SSL alert number 48
```

去掉 `-brief` 运行 s_client，看 `Acceptable client certificate CA names`，就知道服务器接受哪些 CA；然后出示由其中之一签发的证书，并用 `-cert_chain` 带上中间证书。公网 TLS 证书越来越多地不再包含 clientAuth EKU，校验 EKU 的服务器会拒绝它们，见[客户端认证 EKU 被移除](https://ssl.sh/zh/errors/client-auth-eku-removed/)。服务器日志里会有对应的 `certificate verify failed` 及具体原因。
