2、OCSP响应流程:详解客户端发起请求、服务端验证、响应生成的全过程

OCSP的全称是 Online Certificate Status Protocol,说白了就是在线查证书状态。以前用CRL(证书吊销列表)的时候,你得下载一个大列表,然后自己比对——又慢又容易过期。OCSP就好比打一个电话问CA:「这个证书现在还好使吗?」CA在电话那头实时回答你。

但问题来了:这个“打电话”的过程,到底包含了多少步骤?哪个环节可能卡住?我当年接手一个SSL握手频繁超时的项目,日志里全是「OCSP response not received」,查了好几天才发现是CA端的一个缓存配置搞的鬼。嗯,我们一起把OCSP的完整响应流程拆一遍,你就知道该盯哪些点了。

2.1 从客户端发起一次OCSP请求说起

客户端到底是怎么构造OCSP请求的?不是随便发个「ping」就完事。我给你看一个典型的请求结构:

OCSPRequest ::= SEQUENCE {
    tbsRequest          TBSRequest,
    optionalSignature   [0] EXPLICIT Signature OPTIONAL
}

TBSRequest ::= SEQUENCE {
    version            [0] EXPLICIT Version DEFAULT v1,
    requestorName      [1] EXPLICIT GeneralName OPTIONAL,
    requestList        SEQUENCE OF Request
}

Request ::= SEQUENCE {
    reqCert             CertID,
    singleRequestExtensions  [0] EXPLICIT Extensions OPTIONAL
}

CertID ::= SEQUENCE {
    hashAlgorithm       AlgorithmIdentifier,
    issuerNameHash      OCTET STRING,   -- Hash of issuer's DN
    issuerKeyHash       OCTET STRING,   -- Hash of issuer's public key
    serialNumber        CertificateSerialNumber
}

你想想看,这里面最关键的就是 CertID。它不像我们平时发请求那样传一个「证书文件本身」,而是用哈希来标识:签发者名称的哈希、签发者公钥的哈希、证书序列号。三个字段唯一确定一张证书。我一开始也犯过迷糊,以为请求里直接带证书,结果发现请求包其实很小,只有几百字节。

请求生成的步骤:

  • 解析目标证书,提取 Issuer DN 和 Issuer Key
  • 分别计算 SHA-1(或更安全的算法)哈希
  • 组合 SerialNumber 形成 CertID
  • 放入 RequestList(可以同时查多张证书)
  • 封装成 DER 编码,再套一层 HTTP(或直接 ASN.1 over TCP)

至于发送方式,大多数场景走 HTTP POST,Content-Type 为 application/ocsp-request。也有 GET 方式,把请求放到 URL 后面,但我不推荐——长度有限制,而且日志里容易截断,查起来费劲。

发送方式URI 示例适用场景
POSThttp://ocsp.example.com/标准方式,无长度限制
GEThttp://ocsp.example.com/MEMwQTA/...CDN 友好,但日志难跟踪
实战小技巧:调试时在服务端抓包,直接看 POST body 里的完整 OCSP 请求 DER 数据。用 openssl ocsp -reqin 可以解析出来,方便验证客户端组装的 CertID 是否正确。

2.2 服务端:接收、解析、验证

请求到了 CA 或 OCSP 响应器,它会做什么?不是直接查数据库就完事了,这里有个容易被忽略的步骤:

  1. 解析请求:解码 ASN.1,提取 CertID 列表
  2. 查找签发者:用 IssuerNameHash + IssuerKeyHash 匹配内部的签发者记录
  3. 状态查询:根据 SerialNumber 查当前证书状态(good/revoked/unknown)
  4. 签名准备:收集必要字段,准备构建响应

我印象最深的一次性能问题出在第二步——查找签发者。有些老系统把签发者信息放在远程数据库里,每次请求都要跨机房查,延迟直接飙到 200ms。你想想看,TLS 握手等一个 OCSP 来回就多了半秒,用户不投诉才怪。

服务端常见的瓶颈点:

  • 哈希碰撞查找:IssuerKeyHash 如果分布不均匀,索引失效
  • 状态库并发:大规模 CA 每秒可能要处理上万条查询,数据库连接池扛不住
  • 签名计算:每个响应都要重新签名,RSA 2048 位签名一次大概 1-3ms,量一上来 CPU 就吃满了
⚠️ 曾经有个坑:我遇到过 CA 对 unknown 状态不做缓存的逻辑——每次都要全路径查询,结果遇到大规模扫描时直接把数据库打挂了。后来加了分级缓存,unknown 状态也能缓存 30 秒,问题才缓解。

2.3 响应生成与返回

服务端验证完状态之后,要构造一个 OCSPResponse 返回给客户端。响应的灵魂在 BasicOCSPResponse 里:

OCSPResponse ::= SEQUENCE {
    responseStatus         OCSPResponseStatus,
    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL
}

ResponseBytes ::=       SEQUENCE {
    responseType   OBJECT IDENTIFIER,
    response       OCTET STRING  -- DER-encoded BasicOCSPResponse
}

BasicOCSPResponse ::= SEQUENCE {
    tbsResponseData      ResponseData,
    signatureAlgorithm   AlgorithmIdentifier,
    signature            BIT STRING,
    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
}

ResponseData ::= SEQUENCE {
    version              [0] EXPLICIT Version DEFAULT v1,
    responderID          ResponderID,
    producedAt           GeneralizedTime,
    responses            SEQUENCE OF SingleResponse,
    responseExtensions   [0] EXPLICIT Extensions OPTIONAL
}

SingleResponse ::= SEQUENCE {
    certID                       CertID,
    certStatus                   CertStatus,
    thisUpdate                   GeneralizedTime,
    nextUpdate                   GeneralizedTime OPTIONAL,
    singleExtensions         [0] EXPLICIT Extensions OPTIONAL
}

注意到 certStatus 有且只有三种取值:

状态含义响应体行为
good证书目前有效且未被吊销仅标识 good,无额外信息
revoked证书已被吊销包含吊销时间、原因代码
unknown响应器无法确定证书状态客户端应视为证书不可信

为什么会有 unknown?我见过最离谱的,是 CA 把证书信息迁移到新数据库,旧数据库还在但只读,OCSP 服务查不到新发证书,全部返回 unknown——客户端全部拒绝连接,事故持续了 40 分钟。所以你得记得,unknown 不等于「安全」,它意味着查询链路中断了。

响应返回后,客户端要验证签名、检查 producedAt 和 thisUpdate 的时间窗口是否在可接受范围内(一般往前 10 分钟叫 tolerated skew)。如果签名验证失败或响应过期,客户端必须当作证书无效处理。

性能关键指标:从「请求发出」到「响应被客户端验证通过」的总耗时,我一般叫它 OCSP RTT。正常内网 CA 应该在 20ms 以内;跨公网 CA 100-300ms 都算正常。超过 500ms 就要全面排查了——很可能瓶颈出在签名计算或数据库查询上。

好,整个流程走下来就是这样。我们画一张图把它串起来,以后你看日志时就知道:这一步慢了,大概卡在哪个环节。

OCSP 响应全过程流程 OCSP 响应流程(请求—验证—响应) 客户端 构造OCSP请求 发送HTTP POST 服务端 解析ASN.1请求 查询证书状态 构造响应+签名 返回OCSP响应 状态结果 ✔ good · ❌ revoked · ❓ unknown 虚线表示响应带回客户端

这张图上,请求和响应都是双边走完的。看到哪个框慢了,就去看日志里对应的阶段耗时。我一般在调优时最关注「状态查询」和「签名」这两个格子——机器能扛多少并发,基本由它们决定。