OCSP 工作原理:请求/响应流程与签名验证

一次真实的 OCSP 对话

OCSP 说白了就是客户端问 CA:“这张证书现在还活着没?”CA 回答:“活着呢”或“挂了”或“我不知道”。就这么简单。但真实场景里,客户端和 CA 之间要交换的信息远不止“是/否”两个字——背后有一套标准化的协议结构,还带了签名保证你能信得过这个回答。

我早期做 PKI 集成的时候,以为 OCSP 就是发个 HTTP GET 回去就完事了。结果线上环境里,某个客户端死活不信任 OCSP 响应,日志里全是“signature verification failed”。查了半天,原来是 CA 那边的 OCSP 响应器签名证书和客户端信任锚没对上。嗯,从那以后我每次对接都先抓包看签名。

OCSP 请求:客户端到底问什么?

请求报文是 ASN.1 编码的 DER 结构。我用 openssl ocsp 命令造一个例子给你看看:


openssl ocsp -issuer intermediate.pem \
    -cert target.pem \
    -url http://ocsp.example.com \
    -text -noverify

这个命令会构造一个 OCSP 请求,里面包含三个核心字段:

  • 版本号 – 目前只有 v1(值 0)
  • 请求者名称 – 可选。一般留空,不改。
  • 请求列表 – 一组 CertID,包含目标证书的 hash 算法、颁发者名称 hash、颁发者公钥 hash、目标证书序列号。

这里有个关键点:请求里 不传证书本身,只传摘要。为什么?为了隐私。你想想,要是每次查询都明文传证书,中间人就能跟踪谁在查谁。我们只给指纹,CA 也能识别。

我个人习惯:生产环境里,OCSP 请求携带 nonce 扩展。nonce 就是个随机数,防止响应被重放。我见过一个客户,没加 nonce,攻击者把昨天的“revoked”响应重放了一遍,导致合法证书被拒了半个月。

OCSP 响应:CA 的回答长什么样?

响应同样用 ASN.1 编码,结构比请求复杂一些。我拆成三步来理解:

  1. 响应状态 – 最外层:successful 还是 malformedRequest、internalError、tryLater、sigRequired、unauthorized。
  2. 响应主体 – 成功时才出现。包含版本、颁发者名称 hash、应答时间、证书状态列表(good/revoked/unknown),以及可选的扩展。
  3. 签名 – 对响应主体做签名,确保完整性和来源。

下面这张流程图把请求→响应的完整路径画清楚了:

OCSP 请求/响应流程 OCSP 请求/响应流程 客户端 CA OCSP 响应器 OCSP 请求 (CertID) 请求内容 版本 + 请求列表 (hash算法, 颁发者摘要, 序列号) 响应内容 响应状态 + 证书状态 (good / revoked / unknown) + 签名 (CPS, 签名算法, 签名值) OCSP 响应 (签名) 客户端验证流程 ① 检查响应状态 == successful ② 验证签名证书 证书链 + 有效期 + 吊销 ③ 验证 nonce 防止重放攻击 ④ 验证响应签名 用签名证书公钥验签 ⚠ 注意 响应签名证书必须 受客户端信任

签名验证:别被假响应骗了

OCSP 响应如果不带签名,那等于喊了一声“我是好人”——谁都能伪造。所以标准强制要求响应必须签名。但签名不是随便签一签,有一套完整的验证链路。

我按实际排查顺序列一下验证要点:

验证步骤 具体检查 常见翻车点
① 检查响应状态 必须是 successful (0),否则直接丢弃 遇到 tryLater 不应该无限重试,要退避
② 验证签名证书 证书链完整、未过期、未被吊销 CA 换了签名证书,但客户端没更新信任锚
③ 验证 nonce 响应中的 nonce 与请求一致 某些 CA 不支持 nonce,这时要接受无 nonce 的响应
④ 验签 用签名证书公钥验证签名值 签名算法不匹配(比如客户端要求 RSA-SHA256,CA 用了 ECDSA)
⑤ 检查 producedAt 响应时间不能太老(通常不超过 10 分钟) 时间偏差:客户端系统时间不准,导致把合法响应当成过期
我曾经踩过的坑:有一次线上告警说大量证书状态验证失败。我抓包发现 OCSP 响应里证书状态是 good,但客户端就是报错。折腾半天,发现客户端没验证响应签名的证书链——它只检查了签名本身,没检查签名证书是否由受信任的 CA 签发。攻击者可以自己签一个假响应。从那以后,我在代码里加了一条:verify(X509_V_FLAG_PARTIAL_CHAIN) 必须关掉。

几个实战决策

实际部署 OCSP 时,有几个东西需要你根据场景取舍:

  • GET 还是 POST? 大部分 CA 支持 GET(查询参数是 Base64 编码的请求 DER)。POST 可以携带更长的扩展,但我推荐优先用 GET,缓存友好。
  • 是否开启 Stapling? 如果服务端支持 OCSP Stapling,可以大大减少客户端的额外请求。2017 年之后的主流浏览器都要求 Stapling 的响应时效。我个人习惯:能开就开。
  • 超时设置 我一般设 2 秒超时,重试 2 次,失败后走 CRL 兜底。千万别设成 30 秒,用户等不起。
一句话总结:OCSP 的核心就两件事——问得准(CertID 不能错)和信得过(签名必须验完整)。很多线上事故都是签名验证链断了,而不是 CA 真挂了。

好了,这一块你动手抓一次包、自己签一个响应试试,印象会深得多。下次碰到 OCSP 相关的 SSL 握手失败,你就知道从哪里开始查了。