浏览器
unable to get local issuer certificate:服务器缺少中间证书
unable to get local issuer certificate / SEC_ERROR_UNKNOWN_ISSUER缺少中间证书的意思是:服务器只发送了自己的证书,没有附上把它和受信任根证书连起来的 CA 证书。 桌面浏览器常常会悄悄补上,网站看起来一切正常,而 curl、手机 App、Java、Python 会报“unable to get local issuer certificate”之类的错误。
先判断是网站的问题还是你电脑的问题
这一定是服务器配置的问题。用不会自动补链的 SSL 检测 确认:
curl ssl.sh/example.com
结果提示“证书链不完整”,就要改服务器。访客和 App 用户这边没有安全的修法:在一台设备上装中间证书,只是把问题藏起来了。
原因
证书链不完整,最常见的原因是配置了站点证书文件,而不是完整证书链文件。
1. 配置的是站点证书文件,不是完整证书链
Let's Encrypt 的 cert.pem 只有站点证书,fullchain.pem 才是站点证书加中间证书。ssl_certificate 指向 cert.pem 就丢了中间证书。看看服务器发了几张证书:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
结果是 1 就说明只发了站点证书。
2. 商业证书装了,CA bundle 没装
CA 交付时会给站点证书和一个单独的“CA bundle”或“中间证书”文件。Apache 2.4.8 之前用 SSLCertificateChainFile 指定它,Nginx 则需要把两个文件拼在一起。
3. 中间证书用错了或者是旧的
bundle 属于另一个产品线,或者是早就不再签发你这张证书的旧中间证书。看起来链是完整的,实际接不上。对比站点证书的 issuer 和中间证书的 subject 是否一致。
4. 控制台上传时漏了证书链
宝塔面板、阿里云/腾讯云 CDN 和负载均衡的证书上传框,要求把站点证书和中间证书一起粘贴进“证书”一栏。只粘第一段 BEGIN CERTIFICATE 就会丢掉中间证书。
在各浏览器/系统上的样子
| 客户端 | 缺中间证书时的表现 |
|---|---|
| Chrome / Edge / Safari / 360 / QQ 浏览器 | 通常正常,浏览器会通过 AIA 下载中间证书 |
| Firefox | 公开 CA 的证书因为预加载能通过;其他情况报 SEC_ERROR_UNKNOWN_ISSUER |
| 安卓 App | Trust anchor for certification path not found(详情) |
| curl / OpenSSL | unable to get local issuer certificate(详情) |
| Java | PKIX path building failed |
| Python | CERTIFICATE_VERIFY_FAILED |
站长怎么修
修法是让服务器发送完整证书链:站点证书在前,中间证书在后,不放根证书。
1. 用完整证书链文件。 Nginx:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
2. 商业证书按顺序拼接:
cat example.com.crt ca-bundle.crt > example.com.fullchain.crt
Apache 2.4.8 及以后从 SSLCertificateFile 读证书链,指向同一个合并文件即可。从阿里云、腾讯云下载的 Nginx 证书包通常已经合并好,直接用 .pem 或 _bundle.crt 文件。
3. 不知道该用哪张中间证书,把站点证书粘到 证书链工具,它会找到正确的中间证书并按顺序输出。
4. 用严格的客户端验证。 运行 curl ssl.sh/example.com,再在一台没有浏览器缓存的机器上 curl -I https://example.com,都通过才算修好。原理见 证书链。
常见问题
为什么网站在 Chrome 里正常,在我们的 App 里却报证书错误?
Chrome 会根据证书的 AIA 扩展自己下载缺失的中间证书,大多数 App 用的 HTTP 库不会,于是证书链补不全。服务器把中间证书发出来,两边就都正常了。小程序、App 的接口域名尤其要注意。
证书链里要不要放根证书?
不要。客户端的信任库里本来就有根证书,再发一遍浪费流量,个别客户端还会被搞糊涂。只发站点证书加中间证书。
证书文件里的顺序是什么?
先放服务器自己的证书,再放给它签名的中间证书,有更上一级的中间证书就接着往后放。TLS 1.3 能容忍乱序,老客户端不一定。
访客能在自己设备上修吗?
不能。在一台设备上手动装中间证书,只是在这台设备上掩盖了服务器的错误。必须由网站方发送完整证书链。