强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

CA 证书详解:从原理到实践的完整教程 / 第 2 章:工作原理

第 2 章:工作原理

本章深入剖析 TLS 握手过程中证书的验证机制,包括证书链构建、信任锚点匹配、OCSP 在线验证等核心流程。


2.1 TLS 握手概览

TLS(Transport Layer Security)握手是客户端与服务器建立加密连接的过程。证书在其中扮演"身份证明"的角色。

TLS 1.2 握手流程

Client                              Server
  │                                    │
  │──── ClientHello ──────────────────▶│  ① 客户端发送支持的 TLS 版本、
  │                                    │    密码套件列表、随机数
  │◀─── ServerHello ──────────────────│  ② 服务器选定密码套件
  │◀─── Certificate ─────────────────│  ③ 服务器发送证书链
  │◀─── ServerHelloDone ─────────────│
  │                                    │
  │──── ClientKeyExchange ────────────▶│  ④ 客户端生成预主密钥
  │──── ChangeCipherSpec ─────────────▶│  ⑤ 双方切换到加密通信
  │──── Finished ─────────────────────▶│
  │                                    │
  │◀─── ChangeCipherSpec ────────────│
  │◀─── Finished ────────────────────│
  │                                    │
  │◀═══ Application Data (加密) ═════▶│  ⑥ 开始加密通信

TLS 1.3 握手改进

TLS 1.3 将握手简化为 1-RTT(Round-Trip Time),甚至支持 0-RTT 恢复:

Client                              Server
  │                                    │
  │──── ClientHello + KeyShare ───────▶│  ① 密钥交换提前
  │◀─── ServerHello + EncryptedExt ──│  ② 证书被加密传输
  │◀─── Certificate ─────────────────│  ③
  │◀─── CertificateVerify ───────────│  ④
  │◀─── Finished ────────────────────│
  │                                    │
  │──── Finished ─────────────────────▶│
  │                                    │
  │◀═══ Application Data ════════════▶│
特性TLS 1.2TLS 1.3
握手往返次数2-RTT1-RTT
0-RTT 恢复不支持支持(有重放风险)
密码套件数量较多(含不安全算法)精简(仅 AEAD)
证书传输明文加密
前向保密可选强制((EC)DHE)
# 检查服务器支持的 TLS 版本
openssl s_client -connect www.baidu.com:443 -tls1_2 </dev/null 2>/dev/null | grep "Protocol"
openssl s_client -connect www.baidu.com:443 -tls1_3 </dev/null 2>/dev/null | grep "Protocol"

2.2 证书验证过程

当客户端收到服务器的证书时,需要执行一系列验证步骤。

验证清单

收到证书后,客户端依次检查:

  ┌─────────────────────────────────────┐
  │ 1. 证书格式是否合法(ASN.1 解码)     │
  ├─────────────────────────────────────┤
  │ 2. 证书是否在有效期内                 │
  ├─────────────────────────────────────┤
  │ 3. 证书签名是否有效                   │
  ├─────────────────────────────────────┤
  │ 4. 证书链是否能追溯到信任锚点          │
  ├─────────────────────────────────────┤
  │ 5. 域名是否匹配                      │
  ├─────────────────────────────────────┤
  │ 6. 证书是否被吊销(CRL / OCSP)       │
  ├─────────────────────────────────────┤
  │ 7. 证书扩展用途(EKU)是否匹配        │
  └─────────────────────────────────────┘

用 OpenSSL 模拟完整验证

# 完整验证过程
echo | openssl s_client -connect github.com:443 -servername github.com \
  -verify 5 -verify_return_error -status \
  -CApath /etc/ssl/certs 2>&1 | grep -E "Verify|verify|depth|OCSP"

# 输出示例:
# depth=2 C=US, O=DigiCert Inc, CN=DigiCert Global Root G3
# depth=1 C=US, CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
# depth=0 CN=github.com
# Verify return code: 0 (ok)
# OCSP Response Status: successful (0)

逐项验证详解

2.2.1 有效期验证

# 查看证书有效期
echo | openssl s_client -connect expired.badssl.com:443 2>/dev/null \
  | openssl x509 -noout -dates

# 输出:
# notBefore=Apr  9 00:00:00 2015 GMT
# notAfter=Apr 12 23:59:59 2015 GMT
# ↑ 已过期
# 验证过期证书会报错
openssl s_client -connect expired.badssl.com:443 </dev/null 2>&1 | grep "Verify"
# Verify return code: 10 (certificate has expired)

2.2.2 签名验证

签名验证的数学原理:

签名过程(CA 签发时):
  证书数据 → Hash(证书数据) → CA私钥加密 → Signature

验证过程(客户端验证时):
  Signature → CA公钥解密 → Hash'
  证书数据 → Hash(证书数据) → Hash''
  比较 Hash' == Hash'' → 签名有效
# 手动验证签名
# 1. 获取证书
echo | openssl s_client -connect www.baidu.com:443 2>/dev/null \
  | openssl x509 > leaf.pem

# 2. 获取签发者证书(中间证书)
echo | openssl s_client -connect www.baidu.com:443 -showcerts 2>/dev/null \
  | awk '/BEGIN/{n++} n==2' > intermediate.pem

# 3. 验证签名
openssl verify -CAfile intermediate.pem leaf.pem

2.3 证书链构建

AIA(Authority Information Access)

如果服务器没有发送完整的证书链,客户端可以通过证书中的 AIA 扩展信息自动下载缺失的中间证书。

# 查看证书的 AIA 扩展
echo | openssl s_client -connect www.baidu.com:443 2>/dev/null \
  | openssl x509 -noout -text | grep -A2 "Authority Information Access"

# 输出示例:
# Authority Information Access:
#     OCSP - URI:http://ocsp.digicert.com
#     CA Issuers - URI:http://cacerts.digicert.com/DigiCertSecureSiteCNCAG3.crt

⚠️ 注意:依赖 AIA 会导致额外的 HTTP 请求,增加 TLS 握手延迟(约 50-200ms)。最佳实践是在服务端配置完整的证书链。

Path Building 算法

客户端构建证书路径的策略:

策略 1:深度优先(Depth-First)
  从终端证书开始,沿 issuer 字段逐级向上查找

策略 2:广度优先(Breadth-First)
  同时搜索所有可能的中间证书路径

策略 3:信任锚点匹配(Trust Anchor Matching)
  从本地信任存储中的根证书出发,向下匹配

Linux 系统中的路径构建

# 使用 openssl 验证证书链
# -CAfile: 指定信任的根证书
# -untrusted: 指定中间证书
openssl verify \
  -CAfile /etc/ssl/certs/ca-certificates.crt \
  -untrusted chain.pem \
  leaf.pem

# 如果缺少中间证书,可能报错:
# error 2 at 1 depth lookup: unable to get issuer certificate

2.4 信任锚点(Trust Anchor)

信任锚点是整个验证过程的"终点"——一个被客户端预先信任的根证书。

信任锚点的管理

# 查看系统信任锚点数量
ls /etc/ssl/certs/*.pem 2>/dev/null | wc -l
# 输出: 130+ (Ubuntu)

# 列出信任锚点摘要
for cert in /etc/ssl/certs/*.pem; do
  subject=$(openssl x509 -in "$cert" -noout -subject 2>/dev/null)
  if echo "$subject" | grep -q "Root"; then
    echo "$subject"
  fi
done | head -20

自定义信任锚点

# 添加自定义 CA 为信任锚点(Debian/Ubuntu)
sudo cp my-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# 验证
openssl verify /path/to/cert-signed-by-my-ca.pem

2.5 OCSP(Online Certificate Status Protocol)

OCSP 用于在线查询证书的吊销状态,是 CRL(Certificate Revocation List)的替代方案。

OCSP vs CRL

特性CRLOCSP
查询方式下载完整吊销列表实时查询单张证书状态
数据量大(所有吊销证书)小(仅查询的证书)
实时性低(定期更新)高(实时查询)
隐私高(不暴露访问哪个网站)低(CA 知道用户访问了哪个网站)
协议LDAP / HTTPHTTP

OCSP 查询示例

# 获取 OCSP 响应者 URL
echo | openssl s_client -connect www.baidu.com:443 2>/dev/null \
  | openssl x509 -noout -ocsp_uri
# 输出: http://ocsp.digicert.com

# 发送 OCSP 查询
# 1. 提取证书和签发者证书
echo | openssl s_client -connect www.baidu.com:443 -showcerts 2>/dev/null > chain.pem

# 2. 构建 OCSP 请求
openssl ocsp \
  -issuer intermediate.pem \
  -cert leaf.pem \
  -url http://ocsp.digicert.com \
  -resp_text -no_nonce

# 输出示例:
# Response Status: successful (0)
# Cert Status: good         ← 证书有效
# This Update: May  8 00:00:00 2025 GMT
# Next Update: May 15 00:00:00 2025 GMT

OCSP Stapling

OCSP Stapling(OCSP 装订)解决了 OCSP 的隐私和性能问题:由服务器主动获取 OCSP 响应,在 TLS 握手时"钉"在证书旁边发送给客户端。

传统 OCSP:
  客户端 ──▶ OCSP Responder ──▶ "证书有效"
  (每次连接都要查询,CA 知道用户访问了哪些网站)

OCSP Stapling:
  服务器 ──定期──▶ OCSP Responder(获取响应并缓存)
  客户端 ◀─── 证书 + OCSP 响应 ──── 服务器
  (客户端无需额外请求,保护隐私)

Nginx 配置 OCSP Stapling:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate      /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key  /etc/nginx/ssl/privkey.pem;

    # 启用 OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # 指定信任的 CA 证书链(用于验证 OCSP 响应)
    ssl_trusted_certificate /etc/nginx/ssl/chain.pem;

    # DNS 解析器
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}
# 验证 OCSP Stapling 是否生效
echo | openssl s_client -connect example.com:443 -status 2>/dev/null \
  | grep -A2 "OCSP Response"

# 输出:
# OCSP Response Status: successful (0)
# Cert Status: good

2.6 证书吊销机制

CRL(Certificate Revocation List)

# 获取 CRL 分发点
echo | openssl s_client -connect www.baidu.com:443 2>/dev/null \
  | openssl x509 -noout -text | grep -A2 "CRL Distribution Points"

# 下载并查看 CRL
curl -s "http://crl3.digicert.com/DigiCertGlobalRootG3.crl" > ca.crl
openssl crl -in ca.crl -noout -text | head -20

吊销检查的优先级

客户端收到证书后:
  1. 先检查 OCSP Stapling 响应(最快)
  2. 如果没有,查询 OCSP 响应者
  3. 如果 OCSP 不可用,下载 CRL
  4. 如果都不可用(soft-fail),继续连接或拒绝
浏览器默认策略
Chrome使用 CRLSets(本地吊销列表,定期更新),不实时查询
Firefox实时 OCSP 查询,支持 OCSP Stapling
SafariOCSP 查询 + CRL

🔒 安全:Chrome 的 CRLSets 策略意味着它不实时检查证书吊销状态,而是依赖 Google 推送的增量吊销列表。这在安全和性能之间做了一个权衡。


2.7 证书透明度(Certificate Transparency, CT)

CT 是一个开放的框架,用于监控和审计所有公开签发的证书。

CT 的工作原理

CA 签发证书
    │
    ▼
提交到 CT Log(多个独立的日志服务器)
    │
    ▼
获得 SCT(Signed Certificate Timestamp)
    │
    ├── 嵌入到证书中(precertificate SCT)
    ├── 通过 TLS 扩展发送(TLS extension SCT)
    └── 通过 OCSP 响应发送(OCSP SCT)
    │
    ▼
浏览器验证 SCT 是否存在
# 检查证书中的 SCT
echo | openssl s_client -connect www.google.com:443 -ct 2>/dev/null \
  | grep -A5 "SCT"

# 使用 crt.sh 查询域名的 CT 日志
curl -s "https://crt.sh/?q=baidu.com&output=json" | jq '.[0:3] | .[] | {issuer_name, not_before, not_after}'

📋 业务场景:如果有人冒用你的域名向 CA 申请了证书,你可以在 CT 日志中发现这张"幽灵证书"。推荐使用 crt.sh 或 Facebook 的 Certificate Transparency Monitoring 来监控你的域名。


2.8 TLS 握手性能优化

会话复用

机制说明优势
Session ID服务器缓存会话参数客户端可恢复之前的会话
Session Ticket服务器将会话参数加密发给客户端服务器无需维护状态
TLS 1.3 PSKPre-Shared Key 恢复0-RTT 或 1-RTT 恢复

Nginx 配置会话复用

# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

# Session Ticket
ssl_session_tickets on;
# 建议定期轮换 ticket key
ssl_session_ticket_key /etc/nginx/ssl/ticket.key;

Certificate Compression

TLS 1.3 支持证书压缩,减少握手数据量:

# Nginx(需要 OpenSSL 3.0+)
ssl_certificate_compression zlib;

2.9 完整验证示例脚本

#!/usr/bin/env bash
# ssl-verify.sh - 全面验证目标站点的证书状态
# 用法: ./ssl-verify.sh <hostname> [port]

set -euo pipefail

HOST="${1:?用法: $0 <hostname> [port]}"
PORT="${2:-443}"

echo "========================================="
echo " SSL/TLS 证书验证报告: ${HOST}:${PORT}"
echo "========================================="
echo ""

# 1. 获取证书信息
CERT_INFO=$(echo | openssl s_client -connect "${HOST}:${PORT}" \
  -servername "${HOST}" -status 2>/dev/null)

echo "【1. 基本信息】"
echo "$CERT_INFO" | openssl x509 -noout \
  -subject -issuer -dates -serial -fingerprint 2>/dev/null
echo ""

# 2. 验证证书链
echo "【2. 证书链验证】"
VERIFY_RESULT=$(echo | openssl s_client -connect "${HOST}:${PORT}" \
  -servername "${HOST}" -verify 5 -CApath /etc/ssl/certs 2>&1)
echo "$VERIFY_RESULT" | grep -E "Verify|depth"
echo ""

# 3. 证书链详情
echo "【3. 证书链详情】"
echo "$CERT_INFO" | grep -E "^ [0-9] s:|^   i:" 2>/dev/null
echo ""

# 4. OCSP 状态
echo "【4. OCSP 状态】"
if echo "$CERT_INFO" | grep -q "OCSP Response Status: successful"; then
  echo "OCSP Stapling: ✅ 已启用"
  echo "$CERT_INFO" | grep "Cert Status"
else
  echo "OCSP Stapling: ❌ 未启用"
fi
echo ""

# 5. TLS 版本
echo "【5. TLS 版本】"
for ver in tls1_2 tls1_3; do
  if echo | openssl s_client -connect "${HOST}:${PORT}" \
    -servername "${HOST}" -"${ver}" </dev/null 2>/dev/null | grep -q "Protocol.*TLSv"; then
    PROTO=$(echo | openssl s_client -connect "${HOST}:${PORT}" \
      -servername "${HOST}" -"${ver}" </dev/null 2>/dev/null | grep "Protocol" | awk '{print $NF}')
    echo "  ${ver}: ✅ 支持 (${PROTO})"
  else
    echo "  ${ver}: ❌ 不支持"
  fi
done
echo ""

# 6. 密码套件
echo "【6. 协商的密码套件】"
echo "$CERT_INFO" | grep "Cipher is"
echo ""

# 7. 天数检查
echo "【7. 有效期检查】"
NOT_AFTER=$(echo | openssl s_client -connect "${HOST}:${PORT}" \
  -servername "${HOST}" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null \
  | cut -d= -f2)
EXPIRE_EPOCH=$(date -d "$NOT_AFTER" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$NOT_AFTER" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRE_EPOCH - NOW_EPOCH) / 86400 ))

if [ "$DAYS_LEFT" -gt 30 ]; then
  echo "  证书剩余天数: ${DAYS_LEFT} 天 ✅"
elif [ "$DAYS_LEFT" -gt 0 ]; then
  echo "  证书剩余天数: ${DAYS_LEFT} 天 ⚠️ 即将过期!"
else
  echo "  证书已过期 ${DAYS_LEFT#-} 天 ❌"
fi

echo ""
echo "========================================="
echo " 验证完成"
echo "========================================="
# 运行示例
chmod +x ssl-verify.sh
./ssl-verify.sh github.com
./ssl-verify.sh www.baidu.com

2.10 本章小结

主题关键要点
TLS 握手TLS 1.3 简化为 1-RTT,证书在 TLS 1.3 中被加密传输
证书验证包括有效期、签名、链追溯、域名匹配、吊销状态等
证书链构建优先使用服务器提供的链,AIA 作为后备
OCSP Stapling解决 OCSP 隐私和性能问题,应积极启用
CT证书透明度用于监控和审计,防范错误签发

📚 扩展阅读


上一章第 1 章:CA 证书概述 下一章第 3 章:证书类型 — 了解 DV、OV、EV 等各类证书的区别与适用场景。