Web 服务器

安装中间证书,修复证书链不完整

作者: · 最后核实

安装中间证书,就是让服务器在站点证书后面按顺序发送签发它的中间证书。证书链完整后,curl、Java、手机 App、微信小程序这类不会自动补全中间证书的客户端也能正常连接。方法适用于任意 CA,以及 Nginx、Apache 2.4.8+、HAProxy、Tomcat、Node.js 和 IIS。

前提条件

需要站点证书、私钥和服务器的 Shell 权限。

  • 已安装的站点证书(example.com.crt 或 cert.pem)和私钥。
  • 一台能上网的机器上装有 OpenSSL 1.1.1 或 3.x。
  • IIS 需要管理员 PowerShell。

第一步:查看服务器当前发送的证书

用 openssl 列出服务器实际发送的证书,输出里只有深度 0 一条就是缺中间证书:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9] s:|^ *i:'

SSL 证书检查 遇到这种情况会提示“证书链不完整”。

第二步:拿到正确的中间证书

优先用 CA 或 ACME 客户端给的证书包:Certbot 的 fullchain.pem 和 chain.pem、acme.sh 的 fullchain.cer、商业 CA 的 CA bundle,或云厂商下载包里的证书链文件。只有站点证书时,读出 CA Issuers 地址下载签发者证书:

openssl x509 -in example.com.crt -noout -ext authorityInfoAccess
curl -fsSL -o intermediate.der "http://ca-issuers.example.net/intermediate.crt"
openssl x509 -inform DER -in intermediate.der -out intermediate.pem 2>/dev/null || cp intermediate.der intermediate.pem

把 URL 换成第一条命令输出的地址。把证书粘贴到 证书链生成器 也能自动完成这一步。

第三步:按正确顺序合成完整证书链

站点证书在前,后面依次是中间证书,每张都由下一张签发:

cat example.com.crt intermediate.pem > fullchain.pem
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout

输出里每张证书的 issuer 必须等于下一张的 subject。再用系统信任存储验证:

openssl verify -untrusted intermediate.pem example.com.crt

第四步:配置服务器

多数服务器直接读取完整证书链文件,Tomcat 和 IIS 例外。

Nginx:ssl_certificate 指向完整证书链。

ssl_certificate     /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/example.com.key;

Apache 2.4.8+:SSLCertificateFile 使用完整证书链,SSLCertificateChainFile 已经过时。

SSLCertificateFile    /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/example.com.key

HAProxy:一个 PEM 同时包含完整证书链和私钥,cat fullchain.pem example.com.key > /etc/haproxy/certs/example.com.pem。

Tomcat:certificateChainFile 只放中间证书(intermediate.pem),不放站点证书。

Node.js:cert: fs.readFileSync('fullchain.pem')。

IIS:把中间证书导入本机的中级证书颁发机构存储,IIS 会自动发送:

Import-Certificate -FilePath C:\certs\intermediate.cer -CertStoreLocation Cert:\LocalMachine\CA

改完后重载服务(sudo nginx -t && sudo systemctl reload nginx、sudo systemctl reload apache2 等)。宝塔面板里把完整证书链整段粘贴到“证书(PEM 格式)”框即可。

验证

配置正确时,服务器同时发送站点证书和中间证书,检查结果不再提示“证书链不完整”。重载后用 SSL 证书检查 从外部确认,或在终端运行:

curl ssl.sh/example.com

本地的 curl 使用自己的信任存储、不会自动补全中间证书,是很严格的测试:

curl -sS -o /dev/null -w '%{http_code}\n' https://example.com

自动续期

CA 会轮换中间证书,Let's Encrypt 这些年也多次更换证书层级,手工拼好的证书链会随之过时。ACME 客户端每次续期都会给出当前的证书链:服务器直接引用 Certbot 的 fullchain.pem、acme.sh 的 --fullchain-file 或 lego 打包好的 .crt,不要引用单独保存的中间证书。商业证书和云厂商证书每次重新签发后都要重新下载整个证书包。证书最长 200 天(2029 年 3 月 15 日起 47 天),能用 ACME 就自动化,并把域名加到 ssl.sh 监控(开发中),证书链不完整时会提醒。

常见问题排查

常见问题

阿里云、腾讯云下载的证书包含中间证书吗?

Nginx 格式的 .pem 或 .crt 文件通常已经包含站点证书和中间证书,直接用作 ssl_certificate 即可。Apache 格式一般把站点证书和证书链分成两个文件,Apache 2.4.8 及以上可以把两个文件按“站点证书在前”合并后用作 SSLCertificateFile。具体文件名以下载包为准。

证书链里要不要放根证书?

不用。客户端的信任存储里已经有根证书,服务器发送的根证书不参与信任判断,只会让每次握手多传数据。

为什么 Chrome 能打开,curl 和小程序报错?

桌面浏览器能通过 AIA 地址自动补全缺失的中间证书,或复用访问其他网站时缓存的中间证书;curl、Java、大多数手机 App 和微信小程序不会,于是报颁发者未知。见 缺少中间证书。

资料来源

  1. nginx: Configuring HTTPS servers (SSL certificate chains)
  2. Apache httpd: mod_ssl SSLCertificateFile
  3. OpenSSL: openssl-verify
  4. Let's Encrypt: Chains of Trust