PKI 与 TLS 证书生命周期管理

从信任链到自动化轮换与过期治理:详解 PKI 体系结构、X.509 证书与 CSR 签发、ACME 与 cert-manager 自动化、证书透明度与吊销(OCSP Stapling)、mTLS 内部 PKI 及过期监控与告警的完整落地实践。

证书过期导致的全站不可用,几乎每年都会上几次技术新闻。根因很少是"没续期",而是证书生命周期没有自动化、没有监控、没有明确的责任边界。与此同时,TLS 证书早已不只是"给网站配 HTTPS"——服务间 mTLS、代码签名、Kubernetes 准入控制、零信任身份都建立在 PKI 之上。本文系统梳理 PKI 的信任模型、证书的签发与轮换、吊销与透明度,以及如何把整个生命周期管成一条自动化的流水线。

一、PKI 的组成与信任链

公钥基础设施(Public Key Infrastructure,PKI)解决的是"如何信任一个从未见过的公钥"。核心机制是信任链(chain of trust):一个受信的根证书,逐级向下为中间证书、终端实体证书签名。

Root CA(自签名,离线保管,有效期 20 年)
  └── Intermediate CA(在线签发,有效期 5~10 年)
        └── Leaf / End-entity(服务器或客户端证书,有效期 90 天~1 年)

关键规则:

  • 根证书必须离线。一旦根私钥泄露,整个信任体系崩塌,所有下游证书都要重签。
  • 中间证书是消耗品。浏览器只信任根,服务器必须把完整的中间链一并下发,否则部分客户端会报 unable to get local issuer certificate。
  • 信任是终端行为。客户端(浏览器/操作系统)内置了受信根列表(如 Mozilla CA Program),自建 CA 若不在列表里,就必须手动导入。

1.1 X.509 证书里真正重要的字段

字段含义常见坑
Subject主体标识(CN 等)现代校验不看 CN,看 SAN
Subject Alternative Name覆盖的域名/IP漏配即报域名不匹配
Basic Constraints是否 CA、路径长度叶子证书必须 CA:FALSE
Key Usage密钥用途服务器证书需 digitalSignature, keyEncipherment
Extended Key Usage扩展用途服务器 serverAuth,客户端 clientAuth
Not Before / Not After有效期时钟漂移导致 certificate not yet valid
Authority Key Identifier签发者密钥 ID用于在链中选对中间证书

Common Name(CN)在 2017 年后被主流浏览器弃用为域名来源,只作为展示用。所有域名必须写进 SAN,哪怕是单个域名。

二、证书签发:CSR 与 CA

签发的起点是证书签名请求(Certificate Signing Request,CSR)。私钥在本地生成、永不出门,CSR 只包含公钥与主体信息。

2.1 用 openssl 生成密钥与 CSR

# 1. 生成 2048 位(或 4096 位)RSA 私钥,或更现代的 ECDSA
openssl ecparam -genkey -name prime256v1 -out server.key

# 2. 写 CSR 配置文件,把域名放进 SAN
cat > csr.cnf <<'EOF'
[req]
distinguished_name = dn
req_extensions = ext
prompt = no

[dn]
CN = app.example.com

[ext]
subjectAltName = DNS:app.example.com,DNS:www.example.com,IP:10.0.0.5
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
EOF

# 3. 生成 CSR
openssl req -new -key server.key -out server.csr -config csr.cnf

# 4. 自签(仅测试用)或提交给 CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 90 -extfile csr.cnf -extensions ext -out server.crt

2.2 验签与查看

# 查看证书主体、SAN、有效期
openssl x509 -in server.crt -noout -subject -ext subjectAltName -dates

# 验证完整信任链(需要提供中间证书)
openssl verify -CAfile ca-bundle.crt -untrusted intermediate.crt server.crt

# 模拟 TLS 握手,查看服务端下发的链
openssl s_client -connect app.example.com:443 -servername app.example.com -showcerts </dev/null

s_client 是排查线上问题最有效的工具:它能一次性告诉你证书链是否完整、域名是否匹配、协议与密码套件协商结果、是否启用 OCSP Stapling。

2.3 证书与密钥的格式

PKI 工具链里最常见的沟通障碍是"格式对不上"。三类容器要分清:

格式编码内容典型用途
PEMBase64(-----BEGIN...)任意,可含链Nginx、OpenSSL 默认
DER二进制单张证书Java keystore、Windows
PKCS#12(.p12/.pfx)二进制证书 + 私钥 + 链导入浏览器/客户端
PKCS#8私钥容器单个私钥统一的私钥格式

常用转换:

# PEM 证书 → DER
openssl x509 -in server.crt -outform der -out server.der

# 证书 + 私钥 + 链 → PKCS#12
openssl pkcs12 -export -inkey server.key -in server.crt \
  -certfile chain.pem -out bundle.p12 -name "app"

# 传统 RSA 私钥 → 加密的 PKCS#8
openssl pkcs8 -topk8 -in server.key -out server.pk8 -v2 aes-256-cbc

# 从 p12 拆出证书与私钥
openssl pkcs12 -in bundle.p12 -nodes -out all.pem

一个高频事故:把包含私钥的 .pem 误提交进 Git。密钥必须与证书分开存放,并用 密钥与凭证管理 的机制加密托管。

三、自动化签发与轮换

手工签发证书是事故的温床。目标应当是:证书从签发到轮换全自动,人类只在监控告警里出现。

3.1 ACME 与 Let’s Encrypt

ACME(RFC 8555)是自动化签发的事实标准,Let’s Encrypt 是其最流行的实现。核心挑战有两种:

挑战类型原理适用场景
http-01CA 访问 /.well-known/acme-challenge/<token>公网可达的 Web 服务
dns-01在 DNS 加一条 TXT 记录通配符证书、内网服务
tls-alpn-01在 443 端口用特殊 ALPN 响应无法改 Web 配置时
# certbot 手动跑一次,验证流程
certbot certonly --webroot -w /var/www/html \
  -d app.example.com -d www.example.com \
  --email ops@example.com --agree-tos --no-eff-email

# 通配符证书必须用 dns-01
certbot certonly --manual --preferred-challenges dns -d '*.example.com'

Let’s Encrypt 证书有效期 90 天,官方建议每 60 天续期一次。certbot renew 应放进 cron/systemd timer,并配置续期后的 reload 钩子。

3.2 cert-manager 与 Kubernetes

在 Kubernetes 里,cert-manager 把证书抽象成 CRD,自动签发、续期、注入 Secret:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ops@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-account
    solvers:
      - http01:
          ingress:
            class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: app-tls
  namespace: prod
spec:
  secretName: app-tls
  duration: 2160h        # 90 天
  renewBefore: 720h      # 到期前 30 天开始续期
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - app.example.com
    - www.example.com

要点:

  • renewBefore 应设为证书有效期的 1/3 左右,给失败重试留足窗口。
  • 用 ClusterIssuer 而非 Issuer,让所有命名空间共享同一签发配置。
  • 结合 Kubernetes 安全加固 的 RBAC 限制,证书 Secret 只对需要的 ServiceAccount 可见。
  • 监控 certmanager_certificate_expiration_timestamp_seconds 指标,比"到期前 7 天告警"更早发现问题。

3.3 内部 PKI

内网服务不应依赖公共 CA,应自建内部 PKI(可用 Vault PKI、step-ca、cfssl)。内部 CA 的优势:可签发任意长有效期(如服务间 24 小时)、可自定义扩展、可即时吊销。内部根证书同样要离线保管,并定期轮换中间 CA。

3.4 轮换的原子性与热加载

证书文件被替换的瞬间,正在处理的连接不能中断。正确做法是先写临时文件再原子 rename,然后向服务进程发信号触发重载:

# 原子替换:新文件就位后再 rename,避免读到半截文件
cp new.crt /etc/nginx/ssl/app.crt.tmp
mv /etc/nginx/ssl/app.crt.tmp /etc/nginx/ssl/app.crt
nginx -t && nginx -s reload

# HAProxy / Envoy 等支持热加载的组件类似

注意两点:

  • 不要用 cp 直接覆盖正在被读取的文件,可能读到不完整内容。
  • reload 之前先 nginx -t 做语法与证书校验,避免把坏证书推上线导致进程启动失败。

对于不能 reload 的长连接服务,需要在应用层实现"双证书加载"——同时加载新旧证书,按握手时间选择,等旧证书上的连接自然结束后再卸载。

四、证书透明度与吊销

4.1 证书透明度(CT)

CT(Certificate Transparency,RFC 6962)要求 CA 把签发的每张证书登记到公开的、只能追加的日志(CT Log)里。任何人都能审计"某域名被签发了哪些证书",从而发现误签发或恶意签发。现代浏览器要求证书必须携带 SCT(Signed Certificate Timestamp),否则拒绝信任。运维侧可以用 crt.sh 反查自己域名下的所有证书,及时发现异常签发。

4.2 吊销机制对比

机制原理优点缺点
CRL下载完整吊销列表简单列表膨胀、延迟高
OCSP实时查询单张证书状态精确增加延迟、隐私泄露
OCSP Stapling服务端缓存 OCSP 响应随握手下发无额外延迟、保护隐私需服务端正确配置
Must-Staple证书声明必须带 stapling强制生效配置错误即不可用

生产环境应启用 OCSP Stapling:服务端定期向 CA 拉取 OCSP 响应并缓存,握手时直接附带,客户端无需再连 CA。

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

吊销本身是个"尽力而为"的机制——客户端可能因网络或缓存错过吊销信息。因此短有效期正在取代吊销:90 天甚至 24 小时的证书,让"等它自然过期"比"等吊销生效"更快。这也正是自动化轮换如此重要的原因。

4.3 用 CT 反查自己域名的证书

CT 日志对防守方同样有价值:任何人为你的域名申请的证书都会出现在公开日志里。定期巡检可以发现误签发、影子 IT 或钓鱼域名:

# 查询某域名在 CT 日志中登记过的所有证书(crt.sh JSON 接口)
curl -s "https://crt.sh/?q=%25.example.com&output=json" \
  | jq -r '.[] | "\(.not_before)  \(.issuer_name)  \(.name_value)"' \
  | sort -u | head -40

把这条查询接进定时任务,凡是出现未知 CA、未知子域的签发记录就告警。注意结果里 %.example.com 会匹配所有子域,需人工确认哪些属于自己。

五、mTLS 与零信任身份

双向 TLS(mutual TLS,mTLS)要求客户端与服务端互相出示证书。它把"网络位置"从信任依据里剔除,是 零信任微隔离 的基石。

# 用 s_client 验证 mTLS:客户端证书必须被服务端信任
openssl s_client -connect api.internal:8443 \
  -cert client.crt -key client.key \
  -CAfile ca-bundle.crt -servername api.internal

落地要点:

  • 客户端证书有效期要短(小时级),由内部 CA 自动轮换。
  • 校验 SAN/URI:不要只看"证书由我们 CA 签发",还要看它声明的身份是否匹配调用方,否则任意一张内部证书都能冒充所有服务。
  • 服务网格(Istio/Linkerd) 会自动注入 sidecar 并管理证书,证书身份用 SPIFFE ID(spiffe://cluster/ns/prod/sa/orders)表达,把身份绑定到 Kubernetes 的 ServiceAccount。
  • 私钥保护是前提:密钥必须加密存储、限制文件权限,见 现代密码学 中的密钥管理实践。

六、监控与过期治理

证书事故的最后一公里是监控。至少要采集三类指标:

指标来源告警阈值
剩余有效期(天)定期扫描证书< 21 天(warn),< 7 天(crit)
续期任务是否成功certbot/cert-manager连续 2 次失败
握手错误率负载均衡/网关突增即告警

一个简单的探测脚本,扫描一个网段的所有 HTTPS 端点:

#!/usr/bin/env bash
# 输出 "host days_left"
for host in $(cat hosts.txt); do
  end=$(echo | openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null \
        | openssl x509 -noout -enddate | cut -d= -f2)
  left=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
  echo "$host $left"
done

把它接进 Prometheus(写成一个 textfile collector 或 blackbox exporter 探针),就能用统一告警规则覆盖所有证书,包括那些"藏在某个角落、没人记得的"内部服务。

治理层面的三条硬规矩:

  1. 任何证书都必须有 owner 和自动续期,禁止手工导入的长期证书。
  2. 有效期上限:公网证书 ≤ 90 天,内部服务证书 ≤ 24 小时,客户端证书 ≤ 数小时。
  3. 过期演练:定期在预发环境强制让证书过期,验证告警与自动续期是否真的生效。

6.1 常见故障排查表

报错根因处理
certificate has expired未续期或续期失败检查 renew 任务与钩子
unable to get local issuer certificate未下发中间证书拼接 fullchain.pem
hostname mismatchSAN 未包含该域名重签 CSR,域名写进 SAN
certificate not yet valid客户端时钟漂移校准 NTP
self signed certificate客户端不信任自建 CA导入根证书到信任库
no suitable key share协议/套件不兼容检查 TLS 版本与密码套件
握手成功但 mTLS 拒绝客户端证书 SAN 不匹配校验 SAN/URI,而非只看签发者

排查顺序建议固定为:openssl s_client 看链 → openssl verify 验链 → openssl x509 -dates 看有效期 → 对比系统时间。四步能覆盖 90% 的线上证书问题。

小结

PKI 与证书管理的本质是把信任的建立、传递、撤销都变成可自动化的流程。信任链保证"公钥可信",ACME/cert-manager 保证"证书常新",OCSP Stapling 与短有效期保证"失效可控",监控保证"意外可见"。把这四件事都工程化之后,证书就不再是需要人盯的定时炸弹,而是基础设施里一个安静的、自动运转的部件。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「安全」更多文章

  1. SOAR 安全编排自动化与响应
  2. 内部威胁与 UEBA 用户行为分析
  3. 模糊测试与安全测试自动化