LabHub
学习 学习路径 课程

Tomcat 与 nginx 运维

证书故障 90% 出在证书链和过期上

在 LabHub 中继续学习

一句话总结

大多数证书故障与密码学无关,主要来自两件事:证书链缺少中间证书,以及无人关注的到期日。

概念图: 开发人员的电脑上往往无法复现 · 全面故障 · 确认每个文件包含什么的方法 · 密钥与证书不匹配

为什么这是个问题

这两类问题之所以危险,是因为开发人员的电脑上往往无法复现。浏览器会缓存中间证书,或自动下载补全,所以即使服务器漏发证书链,页面也可能正常显示。但服务器之间的集成(Java 客户端、批处理)通常不做这种补偿,会直接失败。“我的浏览器明明可以”就是这样产生的。

到期问题更简单,也更严重。有效期一过,立刻就是全面故障,而且可能从凌晨开始。处理动作只是替换文件,但申请该文件却可能需要数日。所以这不是响应问题,而是监控问题:一个倒数剩余天数的脚本加一条告警就能解决,却每年都有系统因此故障。

SI 现场何时遇到证书

证书通常这样到手:客户安全团队通过邮件发送一个 .pfx 文件及密码,或发来 server.crtserver.keychain.crt 三个文件,偶尔只给一个 .cer,并附一句“请在上线前配置”。

此时需要的不是密码学知识,而是确认每个文件包含什么的方法

# 인증서 내용 보기 (주체, 발급자, 유효기간, SAN)
openssl x509 -in server.crt -noout -subject -issuer -dates -ext subjectAltName

# 키와 인증서가 짝인지 확인 (두 해시가 같아야 함)
openssl x509 -in server.crt -noout -modulus | openssl md5
openssl rsa  -in server.key -noout -modulus | openssl md5

# pfx 를 crt/key 로 분해
openssl pkcs12 -in cert.pfx -clcerts -nokeys  -out server.crt
openssl pkcs12 -in cert.pfx -nocerts -nodes   -out server.key

密钥与证书不匹配时,nginx 甚至无法启动,只留下短短一条 key values mismatch。掌握上面两行命令,30 秒就能确认。

证书链——为什么开发电脑正常,服务器集成失败

证书通常有三层。

Root CA (브라우저·OS 에 이미 들어 있음)
   └─ Intermediate CA (중간 인증서)
        └─ 서버 인증서 (우리 것)

服务器必须同时发送自己的证书 + 中间证书,根证书则由对方预先持有。漏掉中间证书会发生什么?

因此会出现典型症状:“开发人员电脑的浏览器正常,只有对方集成系统报 SSL 错误。” 一行命令即可确认。

echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'

输出 1 说明没有证书链。 正常应为 2 或更多。

nginx 的 ssl_certificate 应指向按服务器证书 → 中间证书顺序拼接的文件,顺序不能反。

cat server.crt intermediate.crt > fullchain.pem

没有 SAN,现代浏览器会拒绝

过去会把域名写在 CN(Common Name)中,如今若没有 **SAN(Subject Alternative Name)**扩展,现代浏览器会拒绝证书,CN 已退居参考用途。

自行签发证书时漏掉 SAN,反复追查“为什么不工作”的情况很常见。为开发/验证环境建立私有 CA 时必须加入 SAN。

subjectAltName = DNS:labhub.local, DNS:*.labhub.local, IP:127.0.0.1

到期——最常见也最荒唐的故障

真实案例中,一次大型 SSO 切换项目因 SAML 签名证书到期,造成 47 分钟全面登录故障。证书到期并非没有预告,因为日期从签发时就已确定;但负责人更换、告警邮件进入垃圾箱后,仍可能无人知晓。

所以,到期监控应由脚本而非人工完成。

openssl x509 -in server.crt -noout -enddate
# notAfter=Nov 12 09:00:00 2026 GMT

# 임계일 이내면 실패로 종료 (checkend 는 초 단위)
openssl x509 -in server.crt -noout -checkend $((30*86400)) \
  || echo "30일 이내 만료"

-checkend 很少被提及,却正适合构建监控脚本。不仅服务器证书,还要把集成对方证书、客户端证书、SAML 签名证书、代码签名证书统一列入清单。没有清单,必然会遗漏一个。

nginx TLS 配置的实务默认值

server {
    listen 8443 ssl;
    server_name labhub.local;

    ssl_certificate     /etc/nginx/tls/fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/server.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers off;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
}

HTTP → HTTPS 重定向

server {
    listen 8088;
    server_name labhub.local;
    return 301 https://$host:8443$request_uri;
}

建议不使用 rewrite,而改用 return 301;后者更快,意图也更清晰。启用重定向后还应考虑 HSTS。不过,HSTS 一旦写入浏览器就很难撤回,内部系统应从较短的 max-age 开始,再逐步增加。