适用读者:遇到
SSLHandshakeException、certificate verify failed、mTLS 配置卡住的人。
前置:S02-B06(HTTP 与 HTTPS)。
实测环境:OpenSSL 3.x / Ubuntu 24.04.1;自签 CA、证书链、验证失败均为 2026-11-05 实测。
覆盖旧文:—(E 驱动;B06 的证书链在此深化)
覆盖扩展:E-S02-023(mTLS)、E-S02-011 深化(TLS 1.3 / 0-RTT)。
上一篇:S02-J01 · TCP 排障与调优:抓包、重传、TIME_WAIT 与内核参数
下一篇:S02-J03 · 实时通信工程。
1. 一句话结论
TLS 报错只有三大类,先分类再动手:
| 类别 | 典型报错 | 病因 |
|---|---|---|
| 链不可信 | unable to get local issuer certificate(num=20) |
中间证书缺失 / 根不受信任 |
| 主机名不匹配 | hostname mismatch(num=62) |
SAN 里没有你访问的域名 |
| 过期/未生效 | certificate has expired(num=10) |
时间不在有效期内 |
外加两类"配置类":协议/套件不匹配、mTLS 客户端证书缺失。
2. 实测:自建 CA,复现两类错误
2.1 生成 CA 与服务器证书
# 1) 自签 CA
openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt -days 2 -subj "/CN=Demo Test CA"
# 2) 服务器证书请求
openssl req -newkey rsa:2048 -nodes -keyout srv.key -out srv.csr -subj "/CN=localhost"
# 3) 用 CA 签发,并注入 SAN
cat > san.cnf <<'EOF'
subjectAltName=DNS:localhost,IP:127.0.0.1
EOF
openssl x509 -req -in srv.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out srv.crt -days 2 -extfile san.cnf
2.2 验证成功(客户端带正确 CA)
openssl s_server -accept 19040 -cert srv.crt -key srv.key -www &
echo | openssl s_client -connect 127.0.0.1:19040 -servername localhost \
-CAfile ca.crt -verify_hostname localhost -verify_return_error
实测输出:
subject=CN = localhost
Verification: OK
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)
2.3 验证失败:客户端不信任这个 CA
echo | openssl s_client -connect 127.0.0.1:19040 -servername localhost -verify_return_error
实测输出:
verify error:num=20:unable to get local issuer certificate
Verification error: unable to get local issuer certificate
Verify return code: 20 (unable to get local issuer certificate)
num=20 的含义:客户端沿着链往上找,找不到签发者的证书——要么中间证书没下发,要么根不在信任库。
2.4 验证失败:主机名不匹配
$ openssl verify -CAfile ca.crt -verify_hostname example.com srv.crt
CN = localhost
error 62 at 0 depth lookup: hostname mismatch
error srv.crt: verification failed
num=62:链是可信的,但证书里的名字(SAN)不包含你访问的名字。
关键:这两类错误修法完全不同——num=20 补链/换信任库,num=62 重新签发证书。先看错误码,别猜。
3. 证书链:为什么"只配服务器证书"会失败
叶证书(你的服务器) ──签──► 中间 CA ──签──► 根 CA(客户端内置)
服务器必须下发"叶 + 中间",根由客户端自带。
| 配置 | 结果 |
|---|---|
| 只下发叶证书 | 客户端可能找到中间证书(有缓存则成功),否则 num=20 |
| 叶 + 中间 | ✅ 标准做法 |
| 叶 + 中间 + 根 | 也能用,但多了几百字节,且根不该由服务器下发 |
Nginx 配置示例:
ssl_certificate /etc/nginx/ssl/fullchain.pem; # 叶 + 中间
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
自检命令(部署后必做):
openssl s_client -connect <域名>:443 -servername <域名> -showcerts | grep -E "^ *[0-9] s:|^ *[0-9] i:"
# 期望:至少两层(叶 → 中间 → 根)
4. TLS 1.3 与 0-RTT 的边界(E-S02-011 深化)
| 特性 | 说明 | 风险 |
|---|---|---|
| 1-RTT 握手 | 首包即带密钥协商,比 1.2 少一轮 | 无 |
| 0-RTT | 复用会话时可在第一个包带数据 | 可被重放 → 只用于幂等请求 |
| 证书加密传输 | 1.3 中证书在加密通道内 | 抓包只能看到 SNI(域名) |
| 强制前向安全 | 只允许 (EC)DHE | 老客户端/老服务端不兼容(需 1.2 兜底) |
配置建议:
ssl_protocols TLSv1.2 TLSv1.3; # 保留 1.2 兜底
ssl_prefer_server_ciphers off; # TLS1.3 下无需指定套件
5. mTLS(双向认证,E-S02-023)
普通 TLS:客户端验证服务端。
mTLS:双方互相验证(服务端也要求客户端出示证书)。
ssl_client_certificate /etc/nginx/ssl/ca.crt; # 用于验证客户端证书的 CA
ssl_verify_client on; # 或 optional
| 项 | 说明 |
|---|---|
| 客户端证书由同一个或受信的 CA 签发 | 否则服务端验证失败 |
服务端要显式开启 ssl_verify_client |
默认不验证客户端 |
证书里要有 clientAuth 用途 |
否则 unsupported certificate purpose |
| 常见错误 | 400 No required SSL certificate was sent → 客户端没带证书 |
适用场景:内部服务间、合作伙伴 API、IoT 设备接入。
不适用:面向公众的网站(用户体验成本太高)。
6. 错误码速查
| 错误码 | 含义 | 处置 |
|---|---|---|
num=10 |
证书过期 | 续期;检查服务器时间 |
num=18 |
自签证书 | 把该 CA 加入信任库(仅测试) |
num=20 |
找不到签发者 | 补中间证书 / 换信任库 |
num=19 |
自签证书在链中 | 同上 |
num=62 |
主机名不匹配 | 证书 SAN 加该域名 |
alert handshake failure |
无共同协议/套件 | 放宽 ssl_protocols/ciphers |
no shared cipher |
同上(服务端) | 检查需 ECDSA 还是 RSA |
unsupported certificate purpose |
证书用途不对 | 重签(加 clientAuth) |
7. 五个误区
| 误区 | 真相 |
|---|---|
| "证书配置好了就一定能连" | 链、主机名、时间、用途四项都要对 |
| "自签证书加到信任库就安全" | 信任库加错根 = 对所有站点失去保护 |
| "TLS 1.3 一定比 1.2 快" | 首次握手快;但兼容性与中间设备支持要验证 |
| "mTLS 就是加个客户端证书" | 还要 CA 链、用途、服务端开关三件配套 |
| "报错是服务端问题" | num=20/62 里,一半是客户端信任库或域名问题 |
8. 自检
- num=20 与 num=62 分别该怎么修?
- 为什么服务器要下发中间证书、而不下发根证书?
- 0-RTT 为什么不能用于下单?
- mTLS 里"Nginx 配了证书但客户端连不上",最可能漏了哪一项?
- 部署新证书后,用哪条命令 30 秒内验证链完整?
9. 下一篇
S02-J03 · 实时通信工程:连接数、心跳、消息可靠性与水平扩展
TLS 之后回到连接本身:一台机器到底能扛多少长连接、内存怎么估、扇出怎么算、怎么水平扩展。下一篇给出估算式与实测约束(fd 上限、内核缓冲)。
本篇属于系列《S02 · 计算机与网络基础》。扩展点 E-S02-011/023。自建 CA、num=20、num=62 均为 2026-11-05 实测。