广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Nginx配置ssl_trusted_certificate时如何正确构建信任证书链?

在部署 HTTPS 服务时,很多人习惯把服务器证书和私钥配置好后就直接上线,直到浏览器或客户端报出证书链不完整、OCSP Stapling 不生效、双向认证失败等问题时才回头检查。Nginx 里的 ssl_trusted_certificate 指令就是处理这类问题的核心,但它和 ssl_certificate 的职责并不相同,真正理解它的用途可以少走很多弯路。

Nginx配置ssl_trusted_certificate时如何正确构建信任证书链?

ssl_trusted_certificate 承担的两种职责

在 Nginx 的 SSL 模块中,ssl_certificate 用来指定服务器向客户端出示的证书,通常是站点证书加中间证书的拼接文件。而 ssl_trusted_certificate 是另一个独立参数,它只用于服务端自己验证证书链的场景:一是启用 ssl_stapling 指令后,Nginx 需要用它来验证 OCSP 响应是否由合法的 CA 签发;二是启用 ssl_verify_client 做双向 TLS 认证时,Nginx 用它作为信任的客户端证书根或中间证书列表。

需要区分:如果只是在单向 HTTPS 下提供站点证书,浏览器报证书链不完整,原因一般出在 ssl_certificate 文件没有按正确顺序包含中间证书,而不是 ssl_trusted_certificate 没配。但是,一旦开启 OCSP Stapling,Nginx 会向 CA 的 OCSP 响应者请求证书状态,然后把这个响应返回给客户端。如果 ssl_trusted_certificate 缺失或证书链不对,Nginx 无法验证 OCSP 响应签名,就会在错误日志中记录 SSL_CTX_use_certificate 或 OCSP_basic_verify 相关错误,并可能回退到不返回 stapling 的状态。

在双向认证里,ssl_trusted_certificate 指定的是服务端信任的 CA 证书文件。客户端证书的签发 CA 必须出现在这个文件中,否则客户端连接时会被拒绝,错误通常表现为 alert unknown ca 或 400 状态码。因此这个文件应该包含所有允许签发客户端证书的根证书和中间证书,按 PEM 格式依次拼接即可。

生成完整的 trusted 证书链文件

先看一个常见场景:你已经拿到服务器证书 server.crt 和私钥 server.key,CA 还提供了中间证书 intermediate.crt 和根证书 root.crt。如果计划开启 OCSP Stapling,需要把中间证书和根证书合并成一个 trusted_chain.crt 文件。注意不要包含服务器证书本身,因为 ssl_trusted_certificate 是用于验证的信任链,不是要发送给客户端的链。

合并命令很简单,使用文本拼接即可:

cat intermediate.crt root.crt > trusted_chain.crt

这里的顺序建议从最靠近服务器证书的中间证书开始,依次向上到根证书。虽然 Nginx 在验证 OCSP 响应时通常会根据证书的 Issuer 字段自动查找匹配的签发者,但保持顺序清晰有助于排查问题。完成后可以用 openssl 检查文件中的证书数量:

openssl crl2pkcs7 -nocrl -certfile trusted_chain.crt | openssl pkcs7 -print_certs -noout

输出应列出 intermediate 和 root 两个证书,而不是把服务器证书混在里面。如果之前错误地把 server.crt 也复制进去,虽然不会破坏语法,但在双向认证场景下可能导致信任模型混乱,所以建议单独维护这个文件。

对于双向 TLS,ssl_trusted_certificate 也可以是客户端 CA 证书。例如企业内部 CA 签发了员工证书,服务端只需要导入该内部 CA 的根证书即可。如果客户端证书链包含两层,则需要同时导入签发客户端证书的中间 CA 和根 CA。

Nginx 配置示例与参数说明

下面是一个完整的 HTTPS 站点配置片段,同时开启 OCSP Stapling 和客户端证书验证:

server {
    listen 443 ssl;
    server_name ipipp.com;
    ssl_certificate     /etc/nginx/ssl/server_chain.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_trusted_certificate /etc/nginx/ssl/trusted_chain.crt;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 1.1.1.1 valid=300s;
    resolver_timeout 5s;
    ssl_verify_client optional;
    ssl_client_certificate /etc/nginx/ssl/client_ca.crt;
    ssl_verify_depth 2;
}

这里 server_chain.crt 是服务器证书加中间证书的拼接,发送给客户端;trusted_chain.crt 是中间证书加根证书的拼接,用于验证 OCSP 响应。ssl_client_certificate 则是用于客户端证书验证的 CA 文件,它与 ssl_trusted_certificate 的功能不同,不要混用。如果只想做单向 HTTPS 并开启 OCSP Stapling,可以去掉 ssl_verify_client 和 ssl_client_certificate 两行。

需要注意 ssl_stapling_verify on 并不是所有 Nginx 版本都支持,通常是 Nginx 1.3.7 以后可用。开启后 Nginx 会严格验证 OCSP 响应,如果 ssl_trusted_certificate 指向的文件不完整,错误日志会频繁出现 OCSP_basic_verify() failed 提示,此时需要按上一节重新生成信任链。

常见错误与验证方法

最典型的错误是 Nginx 启动或重载时提示 BIO_new_file 失败,这通常说明 ssl_trusted_certificate 路径写错或文件不可读。可以先检查文件权限和相对路径,确认以 Nginx 运行用户有读取权限。如果是 systemd 管理的 Nginx,注意 PrivateTmp 等选项不会影响常规证书路径,但 SELinux 或 AppArmor 可能限制访问,需要查看审计日志。

另一种情况是证书链顺序错误。虽然 OpenSSL 库通常能自动排序,但某些老版本或特定场景下需要严格按 Issuer 到 Subject 的顺序排列。可以使用 openssl verify 命令检查信任链是否自洽:

openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt

如果输出 server.crt: OK,说明服务器证书可以通过中间证书验证到根,配置基本正确。验证 OCSP Stapling 是否生效,可以使用 openssl s_client 连接并查看 OCSP response 字段:

echo | openssl s_client -connect ipipp.com:443 -status 2>/dev/null | grep -A 20 "OCSP Response"

如果没有任何输出,可能是 OCSP 响应没有返回或验证失败。此时可以临时关闭 ssl_stapling_verify 观察行为,但生产环境建议保持开启,前提是 trusted_chain 文件正确。

证书链维护与自动化更新建议

证书到期更新时,很多人只替换 server.crt 和 server.key,忘记同步更新 trusted_chain.crt,导致 OCSP Stapling 在新证书部署后立即失效。因此建议把证书链文件的生成步骤写入部署脚本,每次更新证书时自动执行 cat 合并。例如:

cat /etc/ssl/certs/intermediate.crt /etc/ssl/certs/root.crt > /etc/nginx/ssl/trusted_chain.crt
nginx -t && systemctl reload nginx

如果使用 ACME 工具如 certbot 自动续期,注意它默认会生成 fullchain.pem 和 privkey.pem。fullchain.pem 已经包含站点证书和中间证书,但 ssl_trusted_certificate 仍然建议使用 CA 提供的中间和根证书链,不直接复用 fullchain.pem,因为 fullchain.pem 中包含服务器证书,虽然有时也能工作,但语义上并不准确,而且在双向认证场景下可能引入不必要的信任。

对于企业内部的私有机房,根证书通常由内部 CA 签发,更新频率较低,但中间证书可能每年轮换。建议为 trusted 文件建立版本管理,并在 Nginx 配置中指向一个固定路径,通过软链接切换版本,减少 reload 出错的机会。还可以配置监控,定期检查 Nginx 错误日志中的 OCSP 验证失败记录。

广告位 · 文章底部(后台-模块管理替换此区域)