3、证书生命周期管理:证书签发、部署、监控、轮换、吊销的全流程

证书这东西,说白了就是个数字身份证。从出生到销毁,每个环节都得盯紧了。我见过太多团队只关心「证书能不能用」,却从不问「证书什么时候过期」。结果呢?半夜三点被报警叫起来救火。

这一章,我把证书的完整生命周期拆成五个阶段来讲。每个阶段都有坑,也有对应的工具和流程。你跟着走一遍,心里就有底了。

核心观点:证书生命周期管理不是一次性任务,而是一个持续运转的闭环。缺少任何一个环节,都会在某个深夜让你付出代价。

证书生命周期管理五阶段流程图 证书生命周期管理五阶段 ① 签发 ② 部署 ③ 监控 ④ 轮换 ⑤ 吊销 关键说明: • 签发是起点,必须包含密钥生成、CSR 提交、CA 验证、证书颁发四个子步骤。 • 监控和轮换是核心,80% 的证书事故都出在这两个环节。 • 吊销是兜底手段,密钥泄露时必须立即执行,不能犹豫。

3.1 证书签发:从 CSR 到证书文件

签发是第一步,也是最容易出错的环节。我个人习惯把签发流程拆成四个子步骤:密钥生成、CSR 提交、CA 验证、证书颁发。每一步都有讲究。

3.1.1 密钥生成

密钥是证书的根。私钥一旦泄露,证书就是一张废纸。我建议用 OpenSSL 生成 RSA 2048 位或 ECDSA P-256 密钥。别用 1024 位,那玩意儿现在跟纸糊的差不多。

# 生成 RSA 私钥(推荐 2048 位)
openssl genrsa -out server.key 2048

# 生成 ECDSA 私钥(推荐 P-256)
openssl ecparam -genkey -name prime256v1 -out server.key

注意:私钥文件权限必须设置为 600 或 400。我曾经在客户现场发现私钥权限是 644,任何人都能读。那感觉就像把家门钥匙挂在门外墙上。

3.1.2 CSR 提交

CSR 是证书签名请求,里面包含你的公钥和组织信息。Common Name 字段最容易填错。我建议用配置文件来生成 CSR,避免交互式输入带来的拼写错误。

# 使用配置文件生成 CSR
openssl req -new -key server.key -out server.csr -config openssl.cnf

openssl.cnf 示例片段:

[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = MyCompany
CN = www.example.com

[v3_req]
keyUsage = keyEncipherment, digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com

嗯,这里要注意:SAN(Subject Alternative Name)字段现在比 CN 更重要。浏览器已经不看 CN 了,只看 SAN。你如果只填 CN 不填 SAN,证书部署后浏览器会报安全警告。

3.1.3 CA 验证与证书颁发

CA 收到 CSR 后,会验证你的身份。内部 CA 通常走自动化流程,外部 CA 可能需要人工审核。验证通过后,CA 用它的私钥对你的公钥签名,生成证书文件。

我建议内部 CA 用 cfssl 或 step-ca 搭建,支持 API 自动签发。手动用 OpenSSL 签也行,但效率低,容易出错。

# 使用 OpenSSL 自建 CA 签发证书
openssl x509 -req -days 365 -in server.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -extensions v3_req -extfile openssl.cnf

经验之谈:证书有效期别设太长。我以前习惯设 3 年,觉得省事。后来发现,有效期越长,轮换时越容易遗忘。现在内部证书统一设 90 天,配合自动化轮换,反而更安全。

3.2 证书部署:把证书放到正确的位置

证书签下来只是开始,部署才是真正的考验。你想想看,一个 Nginx、一个 Tomcat、一个 Java 应用,证书存放位置和格式都不一样。

我整理了一个常见组件的证书部署对照表:

组件 证书格式 存放路径 注意事项
Nginx PEM /etc/nginx/ssl/ 证书和私钥分开存放,权限 600
Apache PEM /etc/httpd/conf/ssl/ 需要配置 SSLCertificateFile 和 SSLCertificateKeyFile
Tomcat JKS / PKCS12 /opt/tomcat/conf/ 需要设置 keystorePass,建议用 PKCS12 替代 JKS
Java 应用 JKS / PKCS12 应用配置指定 注意信任链证书也要导入 truststore
HAProxy PEM(合并) /etc/haproxy/ssl/ 证书和私钥合并到一个文件,按顺序排列

部署完成后,一定要验证。我习惯用 openssl 命令行直接测试:

# 测试证书是否有效
openssl s_client -connect localhost:443 -servername www.example.com

这条命令会输出证书链、有效期、SAN 等信息。如果输出中有「verify return:1」,说明证书链完整。如果出现「verify error」,那就得排查中间证书是否缺失。

踩坑提醒:我曾经部署完证书后,用浏览器访问一切正常。结果一周后用户反馈部分地区无法访问。查了半天,发现是中间证书没配全,某些旧版 OpenSSL 不自动下载中间证书。从那以后,我每次部署完都会用不同客户端测试一遍。

3.3 证书监控:别等过期了才知道

监控是证书生命周期中最容易被忽视的环节。很多人觉得证书部署完就万事大吉了,结果呢?证书过期导致服务中断的案例,我每年都能遇到好几起。

监控的核心指标就三个:

  • 剩余有效期:低于 30 天触发警告,低于 7 天触发紧急告警
  • 证书链完整性:中间证书是否缺失,根证书是否受信任
  • 域名匹配:证书中的 SAN 是否覆盖所有访问域名

我推荐用 certbot 的 renew 钩子配合监控系统,或者用 blackbox_exporter(Prometheus 生态)做证书过期监控。下面是一个简单的脚本:

#!/bin/bash
# 检查证书剩余天数
CERT_FILE="/etc/nginx/ssl/server.crt"
EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

if [ $DAYS_LEFT -lt 7 ]; then
  echo "CRITICAL: Certificate expires in $DAYS_LEFT days"
  exit 2
elif [ $DAYS_LEFT -lt 30 ]; then
  echo "WARNING: Certificate expires in $DAYS_LEFT days"
  exit 1
else
  echo "OK: Certificate expires in $DAYS_LEFT days"
  exit 0
fi

我的习惯:监控脚本每 6 小时跑一次,结果推送到企业或钉钉。另外,我会在证书到期前 14 天和 3 天各发一次人工确认邮件。双重保障,基本不会漏。

3.4 证书轮换:自动化是关键

轮换是证书生命周期中最频繁的操作。手动轮换不仅慢,而且容易出错。我见过有人轮换时把私钥搞丢了,有人把证书部署到错误的服务器上。

自动轮换的核心思路是:

  1. 监控发现证书即将过期(比如剩余 30 天)
  2. 自动生成新的密钥对和 CSR
  3. 提交给 CA 签发新证书
  4. 将新证书部署到目标服务器
  5. 重新加载服务使新证书生效
  6. 验证新证书是否正常工作
  7. 如果验证失败,自动回滚到旧证书

我推荐用 cert-manager(Kubernetes 环境)或 acme.sh(传统环境)来实现自动轮换。下面是一个 acme.sh 的自动续期示例:

# 安装 acme.sh
curl https://get.acme.sh | sh

# 签发证书(以 Let's Encrypt 为例)
acme.sh --issue -d www.example.com --nginx

# 安装证书到指定目录
acme.sh --install-cert -d www.example.com \
  --key-file /etc/nginx/ssl/server.key \
  --fullchain-file /etc/nginx/ssl/server.crt \
  --reloadcmd "nginx -s reload"

acme.sh 默认会添加 cron 任务,每天检查证书是否需要续期。你基本上不用管它。

关键原则:轮换操作必须支持回滚。我曾经在轮换脚本里忘记加回滚逻辑,结果新证书有个 SAN 没配全,导致线上服务中断了 15 分钟。从那以后,我的轮换脚本第一行就是备份旧证书。

3.5 证书吊销:最后的防线

吊销是证书生命周期中最不常用、但最重要的环节。当私钥泄露、域名变更、员工离职时,必须立即吊销证书。

吊销的方式有两种:

  • CRL(证书吊销列表):CA 定期发布一个列表,包含所有被吊销的证书序列号。客户端需要下载并检查这个列表。
  • OCSP(在线证书状态协议):客户端实时向 OCSP 响应器查询某个证书是否被吊销。响应速度快,但需要 OCSP 服务可用。

我建议优先使用 OCSP,因为它实时性更好。Nginx 可以配置 OCSP Stapling,让服务器自己缓存 OCSP 响应,减少客户端的查询延迟:

# Nginx OCSP Stapling 配置
server {
    listen 443 ssl;
    server_name www.example.com;

    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/ssl/ca-chain.crt;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

紧急情况处理:如果私钥泄露,别犹豫,立即吊销证书。我曾经遇到一次,客户说「先看看情况」,结果 2 小时后攻击者用泄露的私钥伪造了身份。吊销操作越快越好,不要等。

吊销后,记得更新监控系统,把旧证书从监控列表中移除。同时通知所有相关团队,确保没有人还在使用旧证书。

小结

证书生命周期管理,说白了就是五个字:签、发、监、换、销。每个环节都有对应的工具和流程。你只要把这五个环节串起来,形成一个自动化闭环,证书运维就不再是噩梦。

我个人最深的体会是:自动化程度越高,出错的概率越低。手动操作是证书事故的头号原因。所以,能写脚本就别手敲,能上平台就别单机操作。