第2章 OCSP请求构建与调试

OCSP 请求搞不对,后面的所有步骤都是白搭。我见过不少团队花了一整天排查「响应异常」,最后发现是请求本身就没造对。说白了,证书状态查询就像你给 CA 打电话查身份证,电话拨号都拨错了,那对面说啥都是白费。

OCSP请求构建与调试流程图 客户端构建请求 序列号+颁发者 ASN.1 编码 DER 格式 HTTP POST 发送 Content-Type: application/ocsp-request OCSP 响应器 解析 → 查询 → 签名 响应解析 验证签名 + 状态 业务逻辑 good / revoked / unknown 调试关键点 1. DER 编码是否规范(避免填充字节错误) 2. HTTP Header 是否正确(尤其 Content-Type) 3. 请求中的证书序列号是否与目标证书匹配 4. 响应器是否可达(检查防火墙、端口 80/443)

2.1 OCSP 请求长什么样?

一个标准的 OCSP 请求,包装在 DER 编码的 ASN.1 结构里。核心字段就三个:

  • 协议版本:目前只有 0(对应 v1),不需要脑补其他版本。
  • 请求列表:至少包含一个 CertID。每个 CertID 由哈希算法(SHA1/SHA256)、颁发者哈希(DN 哈希)、颁发者公钥哈希、目标证书序列号组成。
  • 扩展:可选,比如 Nonce 防重放。我建议线上环境一定带 Nonce,不然容易被重放攻击。
小技巧:用 openssl ocsp -reqout request.der 可以导出原始请求文件,再配合 openssl asn1parse -in request.der -i 就能看到内部结构。我当年排查一个「请求被拒」的问题,就是靠这招发现哈希算法写成了 MD5——现在是 2025 了,谁还认 MD5?

2.2 构建请求的三种姿势

2.2.1 命令行速查

最快捷的方式:

openssl ocsp -issuer ca.pem -cert target.pem -reqout req.der -no_nonce
openssl ocsp -issuer ca.pem -cert target.pem -url http://ocsp.example.com -respout resp.der

注意 -no_nonce 这会关掉 Nonce 扩展,测试时可以加,生产环境千万别用。我原来有个同事线上也加了这个,导致 OCSP 响应的 Nonce 不匹配,所有客户端都拒了——那晚的宵夜是我请的。

2.2.2 代码库(以 Python 为例)

用 cryptography 库:

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.x509.ocsp import OCSPRequestBuilder

builder = OCSPRequestBuilder()
cert = x509.load_pem_x509_certificate(open('target.pem','rb').read())
issuer = x509.load_pem_x509_certificate(open('ca.pem','rb').read())
builder = builder.add_certificate(cert, issuer, hashes.SHA256())
req = builder.build()
with open('req.der', 'wb') as f:
    f.write(req.public_bytes())

核心就三行:指定证书、颁发者、哈希算法。哈希算法坑最多。我建议统一用 SHA256,因为有些老系统只认 SHA1。要是两边不一致,服务器会直接甩一个 malformedRequest 回来。

2.2.3 手动拼 DER(不推荐,除非你想看底层)

万一你要从零构造(比如嵌入式环境),参考 RFC 6960。记住 SEQUENCE 套 SEQUENCE,每个字段长度都要精确。我之前在工控项目里手写过一次,因为一个长度字节多写了 1,卡了三天。后来用 dumpasn1 对比才发现。

2.3 常见构建错误及排查

错误码 / 现象 根因 排查方法
malformedRequestDER 编码损坏,或版本字段非法asn1parse 对比标准模板
sigRequired请求需要签名但未携带检查是否配置了请求签名凭证
unauthorized请求者不被信任确认客户端证书链是否被响应器信任
HTTP 400 / 405POST 格式错误,或 URL 不对检查 Content-Type 是否是 application/ocsp-request
连接超时网络不可达,或防火墙拦截用 telnet / curl 测试 OCSP 响应器 IP 的 80/443 端口
曾经有学员问我:为什么我发的请求老是被拒,但 Wireshark 看字节流都对?我让他把请求文件发过来,用 openssl ocsp -reqin req.der -text 一读,发现序列号字段用了小端序——证书序列号大部分是大端序,但有些代码库默认是网络序,弄反了。所以请用 openssl 命令做最终验证,别完全信任你写的代码。

2.4 实战:用 Wireshark 抓包确认请求

构建请求发出去后,怎么知道服务器收到了正确的数据?抓包。步骤很简单:

  1. 本机开 Wireshark,过滤 http.request.uri contains "ocsp" 或 ocsp。
  2. 用 curl 模拟请求(前提是你已有 req.der):
    curl -v -H "Content-Type: application/ocsp-request" --data-binary @req.der http://ocsp.example.com/
  3. 看 Wireshark 里的 OCSP Request 层次树,展开每一项对比原始 DER。

如果 Wireshark 解析不出 OCSP 协议(只显示 RAW),说明你的请求格式在它看来就是垃圾数据。我习惯在构造后立即用 openssl ocsp -reqin req.der -text 打印出来,人工检查 certID 里的 issuerNameHash 和 issuerKeyHash 是否与 CA 证书匹配。很多构建错误都是因为哈希对不上——尤其是跨 CA 链的时候,拿错颁发者证书了。

快速检查清单

  • □ 颁发者证书必须与签署目标证书的 CA 一致
  • □ 哈希算法:生产环境统一用 SHA256
  • □ 序列号大小端:确认和证书里打印的一致
  • □ 请求是否携带 Nonce(除非特意不需要)
  • □ HTTP 请求体是裸 DER,不是 PEM 或 Base64

嗯,构建这关过了,后面调试响应才有意义。下个章节我们会把构造好的请求真正发出去,然后看服务器回的是什么妖魔鬼怪——但那些坑,咱们有办法一一填平。