3、OCSP机制详解:OCSP工作原理、OCSP请求/响应格式、OCSP Stapling技术

CRL 有个硬伤——它是个大文件,客户端得下载、解析、比对。在移动设备或者高并发场景下,这体验很差。所以就有了 OCSP(在线证书状态协议)。说白了,OCSP 就是「按需查询」,你问一个证书,它告诉你这个证书现在好不好。

我最早接触 OCSP 是在做一套金融支付系统的时候。当时客户要求交易延迟不能超过 200 毫秒,CRL 那套根本跑不动。嗯,最后就是用 OCSP Stapling 解决的。今天我把这块掰开揉碎讲清楚。

3.1 OCSP 工作原理

OCSP 是一个请求-响应协议。客户端拿着证书,去问 OCSP 响应器:「这个证书吊销了吗?」响应器查一下数据库,告诉你「好」「坏」或者「我不知道」。

整个流程分三步:

  1. 客户端构造请求:提取证书的颁发者信息和序列号,打包成 DER 或 Base64 格式。
  2. 发送给 OCSP 响应器:通常走 HTTP POST 或 GET,URL 从证书的 Authority Information Access (AIA) 扩展里取。
  3. 响应器返回结果:签名后的状态信息,包含证书状态、本次更新时间、下次更新时间。

核心要点:OCSP 响应必须由 CA 或受信任的响应器签名。客户端要验证这个签名,否则中间人可以伪造「证书有效」的响应。

这里有个细节——OCSP 请求本身不签名。为什么?因为请求里没有敏感信息,而且签名会增加客户端开销。但响应必须签名,这是信任的基础。

我曾经遇到过一个问题:某个 CA 的 OCSP 响应器返回了「未知」状态,但证书明明是有效的。查了半天,发现是响应器的时间不同步,导致签名验证失败。所以,NTP 同步对 OCSP 来说不是锦上添花,是刚需。

3.2 OCSP 请求/响应格式

OCSP 使用 ASN.1 编码,底层走 DER。别被 ASN.1 吓到,你不需要手写编码器,但理解结构有助于排错。

3.2.1 请求格式

一个典型的 OCSP 请求包含:

  • 版本:目前只有 v1(值为 0)。
  • 请求列表:可以查多个证书,但实践中一次查一个。
  • 扩展:可选,比如 Nonce 防重放。

每个请求条目包含:

  • 哈希算法:通常是 SHA-1 或 SHA-256。
  • 颁发者名称哈希:对颁发者 DN 做哈希。
  • 颁发者公钥哈希:对颁发者公钥做哈希。
  • 证书序列号:你要查的那个证书的序列号。

避坑指南:我曾经调试过一个 OCSP 问题,客户端一直报「未授权」。后来发现是请求里的颁发者哈希算法和 CA 用的不一致——客户端用 SHA-1,CA 已经升级到 SHA-256。两边对不上,响应器直接拒绝。所以,哈希算法必须对齐。

3.2.2 响应格式

OCSP 响应结构如下:

字段 说明 典型值
responseStatus 协议层状态 successful (0), malformedRequest (1), internalError (2), tryLater (3), sigRequired (5), unauthorized (6)
responseBytes 实际响应数据(仅当 status 为 successful 时存在) BasicOCSPResponse
certStatus 证书状态 good, revoked, unknown
thisUpdate 本次更新时间 2025-01-15T10:30:00Z
nextUpdate 下次更新时间 2025-01-16T10:30:00Z

如果证书被吊销,响应里还会包含吊销时间和吊销原因。原因码包括:

    <
  • unspecified (0):未指定
  • keyCompromise (1):私钥泄露
  • caCompromise (2):CA 被入侵
  • affiliationChanged (3):从属关系变更
  • superseded (4):被替换
  • cessationOfOperation (5):停止运营
  • certificateHold (6):暂时挂起

你想想看,如果响应里返回了 keyCompromise,那意味着什么?意味着这把私钥已经不安全了,必须立即撤销所有信任。

3.2.3 一个实际的 OCSP 交互示例

用 OpenSSL 模拟一下:

# 构造 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: sha256
#           Issuer Name Hash: 47A3...F2B1
#           Issuer Key Hash: 8C5D...E9F0
#           Serial Number: 0x3A1F
# OCSP Response Data:
#     OCSP Response Status: successful (0x0)
#     Response Type: Basic OCSP Response
#     Cert Status: good
#     This Update: Jan 15 10:30:00 2025 GMT
#     Next Update: Jan 16 10:30:00 2025 GMT

注意看,响应里没有「证书有效」这四个字,而是用 good 表示。这是协议层面的约定。

3.3 OCSP Stapling 技术

OCSP 有个性能问题——客户端每次都要额外发起一次 HTTP 请求。如果用户访问一个页面,页面里有 50 个资源(图片、脚本、样式),每个资源都走 HTTPS,那就要做 50 次 OCSP 查询。这延迟谁受得了?

OCSP Stapling 就是来解决这个问题的。它的思路很简单:让服务器替客户端去查 OCSP,然后把结果「订」在 TLS 握手里。

具体流程:

  1. 服务器定期(比如每小时)向 OCSP 响应器查询自己的证书状态。
  2. 服务器把 OCSP 响应缓存起来。
  3. 在 TLS 握手阶段,服务器把缓存的 OCSP 响应通过 Certificate Status 扩展发给客户端。
  4. 客户端验证这个响应,不需要再自己去查 OCSP。

关键优势:OCSP Stapling 把查询次数从 N 次降到了 1 次(服务器查一次,所有客户端复用)。而且客户端不需要直接连接 OCSP 响应器,减少了隐私泄露风险——OCSP 响应器不知道谁在访问哪个网站。

我记得有一次帮客户优化一个电商平台的 TLS 握手时间。开启 OCSP Stapling 后,握手时间从 180ms 降到了 90ms,整整砍掉一半。为什么?因为原来每个客户端都要等 OCSP 响应,现在服务器提前准备好了。

3.3.1 配置示例(Nginx)

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;

    # 指定 OCSP 响应器(可选,默认从证书 AIA 扩展取)
    ssl_trusted_certificate /etc/nginx/certs/ca-chain.crt;

    # 设置解析器(OCSP 查询需要 DNS)
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

配置完可以用 OpenSSL 验证:

openssl s_client -connect example.com:443 -status -tlsextdebug

# 输出中如果有 "OCSP response:" 且状态为 "successful",说明 Stapling 生效了。

注意:OCSP Stapling 不是银弹。如果服务器无法连接到 OCSP 响应器(比如网络故障),stapling 会失败。这时客户端会回退到自己去查 OCSP,或者直接拒绝连接(取决于客户端策略)。所以,一定要监控 OCSP 响应器的可达性。

3.3.2 Must-Staple 扩展

有些 CA 支持在证书里嵌入 Must-Staple 扩展。这个扩展告诉客户端:「这个证书必须使用 OCSP Stapling,否则不要信任。」

为什么要这么做?因为如果没有 Must-Staple,攻击者可以阻断客户端的 OCSP 查询(比如 DNS 劫持),让客户端以为证书是有效的。有了 Must-Staple,服务器必须提供 OCSP 响应,否则握手失败。

我个人习惯在关键业务证书上开启 Must-Staple。虽然配置麻烦一点,但安全性提升了一个档次。

3.4 OCSP vs CRL:选哪个?

我把两者的对比整理了一下:

维度 CRL OCSP OCSP Stapling
实时性 低(依赖更新周期) 高(实时查询) 中(依赖缓存刷新)
网络开销 高(下载完整列表) 中(每次查询) 低(服务器缓存)
隐私保护 好(不暴露查询目标) 差(暴露查询目标) 好(服务器代查)
服务器负载 低(客户端下载) 高(响应器处理每个请求) 中(服务器缓存+响应器查询)
离线支持 支持(可缓存) 不支持 支持(缓存响应)

我的建议是:能用 OCSP Stapling 就用 OCSP Stapling。如果客户端不支持(比如一些老旧的嵌入式设备),回退到 OCSP。CRL 作为兜底方案,但不要作为主要机制。

3.5 本章知识体系

下面这张图把 OCSP 的核心逻辑串起来了:

OCSP 机制核心逻辑图 OCSP 机制核心逻辑 客户端 服务器 OCSP 响应器 流程说明: ① 客户端发起 TLS 握手,请求证书状态 ② 服务器返回证书 + 缓存的 OCSP 响应(Stapling) ③ 客户端验证 OCSP 响应签名,确认证书状态 TLS 握手 后台查询 返回响应 三种模式对比 • 普通 OCSP:客户端直接查响应器,延迟高,隐私差 • OCSP Stapling:服务器代查,延迟低,隐私好 • Must-Staple:强制要求 Stapling,防降级攻击 推荐方案:OCSP Stapling + Must-Staple

嗯,这张图把三种模式的关系和流程都画清楚了。你重点关注 Stapling 那条路径——它是目前生产环境里最实用的方案。

3.6 常见问题与避坑

问题:OCSP 响应过期了怎么办?

服务器应该定期刷新缓存。如果响应过期,客户端会拒绝。我建议把刷新间隔设为 nextUpdate 的一半,留足余量。

问题:OCSP 响应器挂了怎么办?

这是个大坑。如果响应器不可用,客户端的行为取决于实现:有的会「软失败」(认为证书有效),有的会「硬失败」(拒绝连接)。在金融、政务等高安全场景,硬失败是正确选择。但如果你做的是面向公众的网站,软失败可能更合适——总比用户完全访问不了强。

我曾经在一个项目中遇到过 OCSP 响应器被 DDoS 攻击,导致所有客户端无法验证证书。最后我们紧急切换到 CRL 兜底,才恢复了服务。所以,永远要有 Plan B。

好了,OCSP 这块就讲到这里。你理解了工作原理、请求响应格式和 Stapling 技术,就能在生产环境中做出合理的选择了。