3. OCSP 机制详解:在线证书状态协议

CRL 有个硬伤——它是个“大列表”,越下越大。你想想看,一个 CA 签了几百万张证书,CRL 文件动不动就几十兆。客户端每次都要下载整个列表,就为了查一张证书的状态?太浪费了。

OCSP(Online Certificate Status Protocol)就是来解决这个问题的。它把“批量查询”变成了“单点查询”。你只问一张证书,它只回答这张证书。说白了,就是个轻量级的证书状态查询接口。

核心思想:OCSP 让客户端直接问 CA(或授权响应者):“这张证书现在有效吗?”CA 回答:“有效”、“已吊销”或“未知”。一问一答,干净利落。

3.1 OCSP 的工作原理

OCSP 的工作流程其实很简单。我画个图你就明白了:

OCSP 基本工作流程 客户端 OCSP 响应者 ① OCSP 请求(序列号 + 颁发者) ② OCSP 响应(good / revoked / unknown) 关键点 OCSP 响应必须由 CA 或受信任的响应者数字签名 响应默认不包含证书链,客户端需自行验证

具体步骤是这样的:

  1. 客户端发起请求——客户端拿到一张证书后,提取证书的颁发者信息和序列号,构造一个 OCSP 请求,发给 CA 的 OCSP 响应者地址(证书的 Authority Information Access 扩展里就有这个地址)。
  2. 响应者查询状态——OCSP 响应者收到请求后,查自己的数据库或缓存,确认这张证书当前的状态。
  3. 响应者返回结果——响应者把结果(good / revoked / unknown)打包成 OCSP 响应,用私钥签名,返回给客户端。
  4. 客户端验证响应——客户端拿到响应后,验证签名是否有效,确认响应确实来自可信的响应者。

我的经验:OCSP 响应者的地址通常写在证书的 AIA 扩展里。我遇到过好几次,证书里的 AIA 地址写的是内网 IP,结果客户端在外网根本连不上。所以签发证书时,一定要确认 AIA 地址是客户端能访问的。

3.2 OCSP 请求与响应格式

OCSP 用的是 ASN.1 编码,底层走 DER 或 BER。不过你不用手写 ASN.1,大部分语言都有现成的库。我带你看看核心结构就行。

请求格式

一个 OCSP 请求包含以下关键字段:

字段 说明 是否必填
版本 目前只有 v1(值为 0) 是
请求列表 包含一个或多个证书标识(颁发者哈希 + 序列号) 是
请求扩展 可选,比如 nonce 防重放 否
签名 可选,客户端签名(很少用) 否

用 OpenSSL 模拟一个 OCSP 请求,大概长这样:

# 生成 OCSP 请求并发送
openssl ocsp -issuer ca.crt -cert server.crt -url http://ocsp.example.com -text

# 输出示例(简化)
OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
            Hash Algorithm: sha1
            Issuer Name Hash: 0x3A1B2C...
            Issuer Key Hash: 0x4D5E6F...
            Serial Number: 0x1234567890ABCDEF
    Request Extensions:
        Nonce: 0xDEADBEEF...

响应格式

OCSP 响应格式稍微复杂一点,但核心就三个状态:

状态码 含义 说明
good 证书有效 注意:good 不代表证书未被吊销,只代表“在响应者看来,当前状态正常”
revoked 证书已吊销 响应中会包含吊销时间和吊销原因
unknown 未知状态 响应者不认识这张证书,或者查不到记录

响应里还包含一个关键字段——thisUpdate 和 nextUpdate。前者是响应生成时间,后者是建议的下次查询时间。客户端可以缓存响应,直到 nextUpdate 过期。

注意:OCSP 响应默认不包含证书链。客户端拿到响应后,需要自己想办法验证响应者的签名证书是否可信。如果响应者的证书本身已经过期或被吊销,那这个响应就是废纸一张。

3.3 OCSP Stapling 技术

OCSP 有个明显的性能问题——客户端每次建立 TLS 连接,都要额外发一个 HTTP 请求去查证书状态。这增加了握手延迟,也加重了 OCSP 响应者的负担。

OCSP Stapling 就是来解决这个问题的。它的思路很简单:让服务器替客户端去查。

具体流程是这样的:

  1. 服务器(比如 Nginx)定期向 OCSP 响应者查询自己证书的状态。
  2. 响应者返回一个带时间戳的 OCSP 响应。
  3. 服务器把这个响应“订”在 TLS 握手时的 Certificate 消息后面。
  4. 客户端收到证书的同时,也收到了 OCSP 响应,直接验证,不用再额外发请求。

我画个对比图,你一看就明白:

OCSP vs OCSP Stapling 对比 传统 OCSP 客户端 服务器 OCSP 响应者 TLS 握手 OCSP 请求 OCSP 响应 额外 HTTP 请求 → 延迟增加 OCSP Stapling 客户端 服务器 OCSP 响应者 定期查询 缓存响应 TLS 握手 + 响应 零额外请求 → 延迟降低

Nginx 开启 OCSP Stapling 的配置也很简单:

server {
    listen 443 ssl;
    server_name example.com;

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

    # 开启 OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # 指定 CA 证书链(用于验证 OCSP 响应签名)
    ssl_trusted_certificate /etc/nginx/certs/ca-chain.crt;

    # OCSP 响应者地址(可选,默认从证书 AIA 提取)
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

我踩过的坑:有一次配 OCSP Stapling,死活不生效。查了半天,发现是 ssl_trusted_certificate 指向的文件不完整,缺少中间 CA 证书。Nginx 拿不到完整的验证链,就把 Stapling 静默关闭了。记住:这个文件必须包含完整的 CA 链,不能只放根证书。

3.4 OCSP 的优缺点

聊了这么多,咱们来总结一下 OCSP 的优缺点。我在实际项目中用过好几年,体会很深。

优点

  • 实时性好——每次查询都是实时的,不像 CRL 有更新延迟。证书刚被吊销,OCSP 响应者马上就能返回 revoked。
  • 流量小——只查一张证书,响应体很小,通常几百字节。不像 CRL 动不动就几兆甚至几十兆。
  • 隐私性好(相对 CRL)——客户端只告诉响应者“我在查这张证书”,而不是暴露整个证书列表。不过严格来说,OCSP 还是有隐私问题,因为响应者知道你正在访问哪个网站。
  • 支持精细化状态——可以返回吊销原因、吊销时间等详细信息。

缺点

  • 额外延迟——每次 TLS 握手都要多一次 HTTP 请求。虽然 Stapling 解决了这个问题,但并不是所有服务器都支持。
  • 单点故障——OCSP 响应者挂了,客户端就查不到状态。很多客户端会“软失效”(允许连接),这就等于吊销机制失效了。
  • 隐私问题——OCSP 响应者知道你在查哪张证书,也就知道你正在访问哪个网站。虽然比 CRL 好一点,但远谈不上完美。
  • 依赖网络——客户端必须能访问 OCSP 响应者。内网环境、离线环境、网络隔离环境,OCSP 基本用不了。

我的建议:生产环境最好把 OCSP Stapling 和 CRL 结合起来用。Stapling 解决性能问题,CRL 作为兜底。如果客户端既收不到 Stapling 响应,又下载不到 CRL,那就要根据安全策略决定是允许还是拒绝连接。

3.5 避坑指南

最后分享几个我踩过的坑,希望能帮你少走弯路:

  • OCSP 响应者地址写错了——证书里的 AIA 地址一定要测试。我见过有人把测试环境的地址写到了生产证书里,结果客户端全连不上。
  • Stapling 静默失效——Nginx 的 Stapling 如果配置不对,不会报错,只是默默关闭。一定要用 openssl s_client -status 验证。
  • OCSP 响应缓存时间——nextUpdate 字段决定了缓存时长。设得太短,响应者压力大;设得太长,吊销响应不及时。我个人习惯设 4-6 小时。
  • nonce 问题——有些 OCSP 响应者不支持 nonce 扩展。客户端如果强制要求 nonce,反而会拿不到响应。建议做成可选的。

重要提醒:OCSP 不是银弹。如果你的系统对安全要求极高(比如金融、CA 自身),建议同时部署 CRL 和 OCSP,并且做好监控。我曾经见过一个案例,OCSP 响应者挂了三天,没人发现,期间所有吊销的证书都被当作有效证书在使用。