最可靠方法是用bio_new_file("file.crl", "rb")配合d2i_x509_crl_bio()解析der文件,成功后校验签名、遍历x509_crl_get_revoked()获取吊销条目,通过x509_revoked_get0_serialnumber()等提取字段,最后调用x509_crl_free()统一释放。

用 OpenSSL API 解析 DER 编码的 CRL 文件最可靠
直接用 C++ 标准库读二进制文件只能拿到字节流,无法提取 CRL 中的序列号、吊销时间、扩展项等结构化信息。必须借助 OpenSSL 的 ASN.1 解析能力——它把 DER 格式的 CRL 解包成 X509_CRL 对象,后续才能遍历吊销条目。
常见错误是试图用文本方式 fopen + fread 后硬解析,结果遇到 DER 的 TLV 结构就卡死;或者误用 PEM 解析函数(如 PEM_read_bio_X509_CRL)去读 DER 文件,返回空指针且不报错。
- 先用
BIO_new_file()打开文件,注意模式为"rb"(二进制),否则 Windows 下换行符会破坏 DER 数据 - 判断文件是否为 PEM:若首行含
"-----BEGIN X509 CRL-----",用PEM_read_bio_X509_CRL();否则用d2i_X509_CRL_bio() - 解析成功后务必检查返回值是否为
nullptr,并用ERR_print_errors_fp(stderr)查错
遍历吊销条目时注意 REVOKED 链表的内存归属
X509_CRL_get_REVOKED() 返回的是 STACK_OF(X509_REVOKED) 指针,其内部元素由 CRL 对象生命周期管理,不能 free 或 sk_X509_REVOKED_pop_free —— 这会导致二次释放或崩溃。
典型误操作:在循环里对每个 X509_REVOKED* 调用 X509_REVOKED_free(),或把整个栈传给 sk_X509_REVOKED_free()。正确做法是只读取字段,等最终调用 X509_CRL_free() 时统一清理。
- 用
sk_X509_REVOKED_num()获取吊销条目数 - 用
sk_X509_REVOKED_value(sk, i)取第i个条目,再通过X509_REVOKED_get0_revocationDate()和X509_REVOKED_get0_serialNumber()提取数据 - 序列号是
ASN1_INTEGER*,需用i2a_ASN1_INTEGER()转字符串,不能直接当 C 字符串用
处理 CRL 分发点(CRLDP)扩展需手动解析
CRL 文件本身不包含下载地址,但常带 CRL Distribution Points 扩展(OID 2.5.29.31),里面存着 HTTP/FTP URL。OpenSSL 不提供现成函数提取它,得手动走 X509_CRL_get_ext_d2i() 流程。
容易漏掉的是:该扩展是可选的,X509_CRL_get_ext_d2i() 返回 nullptr 很正常;且返回的 STACK_OF(DIST_POINT) 里每个 DIST_POINT 的 distpoint 字段可能是 fullname(URI)或 nameRelativeToCRLIssuer(相对名),后者需拼接 issuer DN,实际中几乎见不到。
- 调用
X509_CRL_get_ext_d2i(crl, NID_crl_distribution_points, &crit, &idx),crit输出是否关键扩展 - 遍历
STACK_OF(DIST_POINT),对每个dp检查dp->distpoint && dp->distpoint->type == DIST_POINT_FULLNAME - 再遍历
dp->distpoint->name.fullname(GENERAL_NAMES),对每个GENERAL_NAME判断gen->type == GEN_URI,然后用ASN1_STRING_get0_data(gen->d.uniformResourceIdentifier)取 URL
别忽略 CRL 签名验证这一步
读到吊销列表不等于可信——攻击者可伪造 CRL 文件。必须用颁发该 CRL 的 CA 证书(通常是签发终端证书的那个 CA)调用 X509_CRL_verify() 验证签名。否则,解析出的数据毫无安全意义。
常见疏忽:用错证书(比如用了终端证书而非 CA 证书)、没加载 CA 的公钥、或忽略 X509_CRL_verify() 返回值(失败时返回 0 或负数)。OpenSSL 1.1.1+ 默认启用严格检查,旧版需手动调 X509_CRL_set_version() 确保版本兼容。
- 确保 CA 证书已用
PEM_read_bio_X509()正确加载,并验证其有效性(非过期、未被吊销) -
X509_CRL_verify(crl, x509_ca->pubkey)成功才继续后续逻辑 - 若验证失败,
ERR_print_errors_fp(stderr)通常会输出类似"error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01",说明签名不匹配或密钥错误
真正难的不是读文件,而是把 ASN.1 结构、内存生命周期、扩展解析和密码学验证这几层都串对。少一个环节,数据就不可信。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











