CA 证书详解:从原理到实践的完整教程 / 第 5 章:证书管理
第 5 章:证书管理
证书管理贯穿证书的整个生命周期——从添加、更新、信任到吊销和黑名单。本章介绍系统级和应用级的证书管理实践。
5.1 添加受信任证书
添加到系统信任存储
# Debian/Ubuntu
# 1. 将证书文件复制到指定目录
sudo cp enterprise-root-ca.crt /usr/local/share/ca-certificates/
# 2. 更新证书存储
sudo update-ca-certificates
# 输出: 1 added, 0 removed; done.
# 3. 验证
openssl verify -CApath /etc/ssl/certs /path/to/cert-signed-by-ca.pem
# RHEL/CentOS/Fedora
sudo cp enterprise-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
# 验证
trust list | grep "Enterprise"
添加多个 CA 证书
# 批量添加
for crt in /path/to/ca-bundle/*.crt; do
sudo cp "$crt" /usr/local/share/ca-certificates/
done
sudo update-ca-certificates
# 查看添加结果
ls -la /etc/ssl/certs/ | grep "$(date +%Y)"
添加证书链
# 添加包含完整证书链的文件
cat root-ca.crt intermediate-ca.crt > full-chain-ca.crt
sudo cp full-chain-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
5.2 删除/移除证书
从系统信任存储移除
# Debian/Ubuntu
# 方法 1:直接删除文件
sudo rm /usr/local/share/ca-certificates/my-ca.crt
sudo update-ca-certificates
# 方法 2:在配置文件中禁用(保留文件)
sudo vim /etc/ca-certificates.conf
# 将 "mozilla/Some_CA.crt" 改为 "!mozilla/Some_CA.crt"
sudo update-ca-certificates
# RHEL/CentOS
sudo rm /etc/pki/ca-trust/source/anchors/my-ca.crt
sudo update-ca-trust extract
# 使用 trust 工具
sudo trust anchor --remove my-ca.crt
移除后的影响
| 影响范围 | 说明 |
|---|
| curl / wget | 立即生效,无法验证由该 CA 签发的证书 |
| 浏览器 | 需要重启浏览器 |
| Python requests | 使用 certifi 的需要重启应用 |
| Java | 需要从 cacerts 中移除 |
5.3 证书更新/续期
手动更新证书
# 1. 生成新的 CSR
openssl req -new -key server.key -out server-new.csr \
-subj "/C=CN/ST=Beijing/O=MyOrg/CN=example.com"
# 2. 提交给 CA 签发(或使用 ACME 自动化)
# 3. 部署新证书
sudo cp new-cert.pem /etc/nginx/ssl/cert.pem
sudo cp new-chain.pem /etc/nginx/ssl/chain.pem
# 4. 重新加载服务
sudo nginx -t && sudo nginx -s reload
# 5. 验证新证书
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates -serial
自动续期(certbot)
# 测试续期
sudo certbot renew --dry-run
# 设置自动续期(systemd timer)
sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer
# 查看定时器状态
sudo systemctl status certbot-renew.timer
# 或使用 cron
echo "0 3 * * 1 root certbot renew --quiet --post-hook 'nginx -s reload'" \
| sudo tee /etc/cron.d/certbot-renew
Nginx 零停机证书更新
# 使用 nginx -s reload 实现零停机更新
# reload 不会中断现有连接
# 配置示例
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
}
# 更新证书后
sudo nginx -t && sudo nginx -s reload
# 现有连接不受影响,新连接使用新证书
5.4 证书黑名单
为什么需要黑名单
- CA 被攻破或不再受信任
- 特定中间 CA 存在安全问题
- 某些 CA 的行为不符合安全标准
添加到黑名单
# Debian/Ubuntu
# 方法 1:在 ca-certificates.conf 中禁用
sudo vim /etc/ca-certificates.conf
# 添加: !mozilla/Compromised_CA.crt
sudo update-ca-certificates
# 方法 2:删除证书文件
sudo rm /usr/share/ca-certificates/mozilla/Compromised_CA.crt
sudo update-ca-certificates
# RHEL/CentOS
# 将证书放入 blacklist 目录
sudo cp compromised-ca.crt /etc/pki/ca-trust/source/blacklist/
sudo update-ca-trust extract
# 验证
trust list | grep "Compromised"
# 应该不再出现
浏览器级黑名单
| 浏览器 | 黑名单机制 | 说明 |
|---|
| Chrome | CRLSets | Google 推送的增量吊销列表 |
| Firefox | OneCRL | Mozilla 维护的吊销列表 |
| Safari | 系统级 OCSP/CRL | 跟随 macOS 信任策略 |
真实案例:Symantec CA 事件
时间线:
2015 - Google 发现 Symantec 签发了未授权的证书
2017 - Google 宣布逐步不信任 Symantec 证书
2018 - Chrome 70 开始不信任 Symantec 签发的证书
2018 - DigiCert 收购 Symantec 证书业务
影响:
- 所有由 Symantec 旧基础设施签发的证书需要替换
- 大量网站需要更新证书链
- DigiCert 重新签发了受影响的证书
5.5 证书透明度日志(CT Log)监控
什么是 CT Log
CT(Certificate Transparency)是一个开放的框架,所有公开信任的 CA 必须将签发的证书提交到公开的日志服务器。
查询 CT Log
# 使用 crt.sh 查询域名的证书记录
curl -s "https://crt.sh/?q=example.com&output=json" | \
jq '.[] | {id, issuer_name, name_value, not_before, not_after}' | head -30
# 查询最近 30 天的证书
curl -s "https://crt.sh/?q=example.com&output=json" | \
jq --arg since "$(date -d '30 days ago' +%Y-%m-%d)" \
'[.[] | select(.not_after > $since)] | length'
# 查询特定子域名
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
jq '.[].name_value' | sort -u
使用 CT Log 发现异常证书
#!/usr/bin/env bash
# ct-monitor.sh - 监控 CT 日志中的新证书
# 用法: ./ct-monitor.sh <domain> [known_domains_file]
DOMAIN="${1:?用法: $0 <domain> [known_domains_file]}"
KNOWN_FILE="${2:-}"
# 获取 CT 日志中的所有域名
FOUND=$(curl -s "https://crt.sh/?q=%.${DOMAIN}&output=json" 2>/dev/null \
| jq -r '.[].name_value' | sort -u)
if [ -n "$KNOWN_FILE" ] && [ -f "$KNOWN_FILE" ]; then
# 对比已知域名列表,发现未知证书
NEW=$(comm -23 <(echo "$FOUND") <(sort "$KNOWN_FILE"))
if [ -n "$NEW" ]; then
echo "⚠️ 发现未知域名的证书:"
echo "$NEW"
# 可接入告警系统
else
echo "✅ 未发现异常证书"
fi
else
echo "域名 ${DOMAIN} 的 CT 日志记录:"
echo "$FOUND"
fi
chmod +x ct-monitor.sh
./ct-monitor.sh example.com known-domains.txt
CT Log 服务器
| 日志服务器 | 运营方 | URL |
|---|
| Google Argon | Google | ct.googleapis.com/logs/argon2025h1 |
| Google Xenon | Google | ct.googleapis.com/logs/xenon2025h1 |
| Cloudflare Nimbus | Cloudflare | ct.cloudflare.com/logs/nimbus2025h1 |
| DigiCert Yeti | DigiCert | yeti2025.ct.digicert.com/log |
# 查询 CT Log 的状态
curl -s "https://ct.googleapis.com/logs/argon2025h1/ct/v1/get-sth" | jq .
5.6 证书吊销管理
CRL 吊销列表管理
# 查看证书的 CRL 分发点
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -text | grep -A4 "CRL Distribution"
# 下载 CRL
curl -s "http://crl.example.com/ca.crl" -o ca.crl
# 查看 CRL 内容
openssl crl -in ca.crl -noout -text | head -30
# 检查特定证书是否在 CRL 中
openssl crl -in ca.crl -noout -text | grep -A2 "Serial Number: 1234ABCD"
OCSP 吊销查询
# 获取 OCSP 响应者 URL
OCSP_URL=$(echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -ocsp_uri)
# 查询证书状态
openssl ocsp \
-issuer intermediate.pem \
-cert leaf.pem \
-url "$OCSP_URL" \
-resp_text
# 可能的状态:
# good - 证书有效
# revoked - 证书已吊销
# unknown - 未知证书
5.7 证书管理自动化脚本
批量证书状态检查
#!/usr/bin/env bash
# cert-batch-check.sh - 批量检查证书状态
# 用法: ./cert-batch-check.sh hosts.txt
HOSTS_FILE="${1:?用法: $0 <hosts_file>}"
WARN_DAYS=30
printf "%-30s %-12s %-12s %-8s %s\n" "HOST" "NOT_BEFORE" "NOT_AFTER" "DAYS" "STATUS"
printf "%-30s %-12s %-12s %-8s %s\n" "----" "----------" "---------" "----" "------"
while IFS= read -r host || [ -n "$host" ]; do
[ -z "$host" ] && continue
# 获取证书信息
CERT_INFO=$(echo | openssl s_client -connect "${host}:443" -servername "$host" \
-verify 5 -CApath /etc/ssl/certs 2>/dev/null)
VERIFY=$(echo "$CERT_INFO" | grep "Verify return code" | head -1)
DATES=$(echo "$CERT_INFO" | openssl x509 -noout -dates 2>/dev/null)
NOT_BEFORE=$(echo "$DATES" | grep notBefore | cut -d= -f2)
NOT_AFTER=$(echo "$DATES" | grep notAfter | cut -d= -f2)
if [ -n "$NOT_AFTER" ]; then
EXPIRE_EPOCH=$(date -d "$NOT_AFTER" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRE_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -gt "$WARN_DAYS" ]; then
STATUS="✅ OK"
elif [ "$DAYS_LEFT" -gt 0 ]; then
STATUS="⚠️ 即将过期"
else
STATUS="❌ 已过期"
fi
printf "%-30s %-12s %-12s %-8d %s\n" \
"$host" \
"$(date -d "$NOT_BEFORE" +%Y-%m-%d 2>/dev/null)" \
"$(date -d "$NOT_AFTER" +%Y-%m-%d 2>/dev/null)" \
"$DAYS_LEFT" \
"$STATUS"
else
printf "%-30s %-12s %-12s %-8s %s\n" \
"$host" "-" "-" "-" "❌ 连接失败"
fi
done < "$HOSTS_FILE"
# 创建主机列表
cat > hosts.txt << 'EOF'
www.baidu.com
github.com
www.google.com
expired.badssl.com
EOF
# 运行检查
chmod +x cert-batch-check.sh
./cert-batch-check.sh hosts.txt
证书过期告警
#!/usr/bin/env bash
# cert-alert.sh - 证书过期告警
# 用法: ./cert-alert.sh hosts.txt [warn_days] [alert_hook_url]
HOSTS_FILE="${1:?用法: $0 <hosts_file> [warn_days] [alert_hook_url]}"
WARN_DAYS="${2:-30}"
HOOK_URL="${3:-}"
ALERT_HOSTS=()
while IFS= read -r host || [ -n "$host" ]; do
[ -z "$host" ] && continue
NOT_AFTER=$(echo | openssl s_client -connect "${host}:443" -servername "$host" \
2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -n "$NOT_AFTER" ]; then
EXPIRE_EPOCH=$(date -d "$NOT_AFTER" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRE_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -le "$WARN_DAYS" ]; then
ALERT_HOSTS+=("${host}: ${DAYS_LEFT} 天")
fi
fi
done < "$HOSTS_FILE"
if [ ${#ALERT_HOSTS[@]} -gt 0 ]; then
MESSAGE="⚠️ 以下证书即将过期或已过期:\n"
for item in "${ALERT_HOSTS[@]}"; do
MESSAGE+=" - ${item}\n"
done
echo -e "$MESSAGE"
# 发送到 Webhook(如钉钉、飞书、企业微信)
if [ -n "$HOOK_URL" ]; then
curl -s -X POST "$HOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$(echo -e "$MESSAGE")\"}}"
fi
fi
5.8 证书管理最佳实践
组织级证书管理清单
□ 建立证书清单(所有使用的证书列表)
□ 设置证书过期监控(提前 30/14/7 天告警)
□ 自动化证书续期(certbot / ACME)
□ 定期审计 CT 日志(发现未授权证书)
□ 建立应急响应流程(私钥泄露时的处理步骤)
□ 记录证书的密钥管理方式(HSM / Vault / 文件)
□ 定期轮换证书(建议每年至少一次)
□ 备份私钥和证书链(加密存储)
证书清单表
| 域名 | 证书类型 | CA | 签发日期 | 过期日期 | 服务器 | 管理方式 |
|---|
| www.example.com | OV Wildcard | DigiCert | 2025-01-01 | 2026-01-01 | Nginx | certbot |
| api.example.com | DV | Let’s Encrypt | 2025-04-01 | 2025-07-01 | Nginx | certbot |
| mail.example.com | OV | Sectigo | 2024-06-01 | 2025-06-01 | Postfix | 手动 |
5.9 本章小结
| 主题 | 关键要点 |
|---|
| 添加证书 | 放入指定目录后执行更新命令 |
| 移除证书 | 删除文件或在配置文件中禁用 |
| 证书续期 | 推荐使用 certbot 自动续期 |
| 黑名单 | 用于撤销对特定 CA 的信任 |
| CT Log 监控 | 发现未授权签发的证书 |
| 自动化 | 批量检查 + 告警脚本 |
📚 扩展阅读
上一章:第 4 章:系统证书存储
下一章:第 6 章:OpenSSL 工具 — 掌握 OpenSSL 的核心命令,生成 CSR、自签名、验证和格式转换。