第2章 OCSP请求构建与调试
OCSP 请求搞不对,后面的所有步骤都是白搭。我见过不少团队花了一整天排查「响应异常」,最后发现是请求本身就没造对。说白了,证书状态查询就像你给 CA 打电话查身份证,电话拨号都拨错了,那对面说啥都是白费。
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 常见构建错误及排查
| 错误码 / 现象 | 根因 | 排查方法 |
|---|---|---|
malformedRequest | DER 编码损坏,或版本字段非法 | asn1parse 对比标准模板 |
sigRequired | 请求需要签名但未携带 | 检查是否配置了请求签名凭证 |
unauthorized | 请求者不被信任 | 确认客户端证书链是否被响应器信任 |
| HTTP 400 / 405 | POST 格式错误,或 URL 不对 | 检查 Content-Type 是否是 application/ocsp-request |
| 连接超时 | 网络不可达,或防火墙拦截 | 用 telnet / curl 测试 OCSP 响应器 IP 的 80/443 端口 |
openssl ocsp -reqin req.der -text 一读,发现序列号字段用了小端序——证书序列号大部分是大端序,但有些代码库默认是网络序,弄反了。所以请用 openssl 命令做最终验证,别完全信任你写的代码。
2.4 实战:用 Wireshark 抓包确认请求
构建请求发出去后,怎么知道服务器收到了正确的数据?抓包。步骤很简单:
- 本机开 Wireshark,过滤
http.request.uri contains "ocsp"或ocsp。 - 用 curl 模拟请求(前提是你已有
req.der):curl -v -H "Content-Type: application/ocsp-request" --data-binary @req.der http://ocsp.example.com/ - 看 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
嗯,构建这关过了,后面调试响应才有意义。下个章节我们会把构造好的请求真正发出去,然后看服务器回的是什么妖魔鬼怪——但那些坑,咱们有办法一一填平。