登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go crypto/x509.ParseRevocationList 怎样读取 CRL 撤销条目

来源:17golang原创

时间:2026-11-10 23:01:57 471浏览 收藏

处理证书吊销列表(CRL)时,真正传给 x509.ParseRevocationList 的不是整段 PEM 文本,而是去掉 PEM 外壳后的 ASN.1 DER 字节。解析成功后得到 *x509.RevocationList,可以读取签发者、更新时间和被撤销证书条目。

最容易混淆的地方有三个:PEM 与 DER 是两层格式;解析 CRL 不等于验证签名;旧的 ParseDERCRL 返回 pkix.CertificateList,新代码应优先使用 ParseRevocationList。

落地结论
  • 先用 pem.Decode 取出 block.Bytes,再传给 ParseRevocationList。
  • 读取条目时优先看 RevokedCertificateEntries,可得到序列号、撤销时间和原因码。
  • 解析只说明编码结构可读;需要确认签发者时,再单独调用 CheckSignatureFrom。

一、先把 PEM 外壳和 DER 内容分开

CRL 文件经常以 -----BEGIN X509 CRL----- 开头。这个文本外壳便于传输,但标准库解析函数接收的是 DER 内容。若把整段 PEM 字符串直接转成字节传入,通常会得到 ASN.1 解析错误。

Go 读取 CRL 时从 PEM 外壳提取 DER 内容再进入 ParseRevocationList 的静态流程说明图
图1:CRL 输入结构说明图,观察 PEM 外壳、DER 字节与 ParseRevocationList 的输入关系。
func decodeCRL(data []byte) ([]byte, error) {
	// PEM 只是编码外壳,先取出其中真正的 DER 字节。
	block, rest := pem.Decode(data)
	if block == nil {
		return nil, errors.New("没有找到 PEM 数据块")
	}
	// 允许调用方检查文件中是否意外拼接了第二段内容。
	if len(bytes.TrimSpace(rest)) != 0 {
		return nil, errors.New("CRL 后仍有未处理的数据")
	}
	if block.Type != "X509 CRL" {
		return nil, fmt.Errorf("不是 X509 CRL: %s", block.Type)
	}
	return block.Bytes, nil
}

如果上游已经提供 DER 文件,就不需要 pem.Decode,直接把文件字节交给解析函数即可。输入层完成后,后面的代码不应再关心文件是 PEM 还是 DER。

二、用 ParseRevocationList 得到结构化 CRL

ParseRevocationList 解析的是 X.509 v2 CRL 的 ASN.1 DER 数据,返回 *x509.RevocationList。它能提供 Issuer、ThisUpdate、NextUpdate、编号以及撤销条目等结构化字段。

func parseCRL(data []byte) (*x509.RevocationList, error) {
	// 解析阶段只负责确认 DER 结构可读,不代表签名已经可信。
	der, err := decodeCRL(data)
	if err != nil {
		return nil, err
	}
	list, err := x509.ParseRevocationList(der)
	if err != nil {
		return nil, fmt.Errorf("解析 CRL 失败: %w", err)
	}
	return list, nil
}

旧代码可能还在使用已弃用的 ParseDERCRL。迁移时不要只把函数名替换掉就结束:返回对象的字段模型也应改为 x509.RevocationList,并明确后续是读取条目还是做签名检查。

三、读取撤销条目的序列号、时间和原因

一张 CRL 可以包含多个被撤销证书。对每个条目,最常用的信息是证书序列号和撤销时间;如果发布方带有原因码,还可以读取 ReasonCode。Go 1.21 起,RevocationListEntry 提供了更适合新代码使用的条目类型。

Go RevocationList 拆分为多个撤销条目并读取序列号时间原因码的静态关系说明图
图2:撤销条目关系说明图,查看一张 CRL 如何对应多个序列号、时间和原因字段。
func summarize(list *x509.RevocationList) []string {
	result := make([]string, 0, len(list.RevokedCertificateEntries))
	for _, entry := range list.RevokedCertificateEntries {
		// 序列号用于和待检查证书建立对应关系,时间用于判断撤销发生时点。
		result = append(result, fmt.Sprintf(
			"serial=%s revoked_at=%s reason=%d",
			entry.SerialNumber.Text(16),
			entry.RevocationTime.Format(time.RFC3339),
			entry.ReasonCode,
		))
	}
	return result
}

不要把 RevokedCertificateEntries 当成“当前仍然有效的黑名单”直接长期缓存。它表达的是这张 CRL 发布时包含的撤销记录,业务还需要结合 ThisUpdate、NextUpdate 和自己的缓存刷新策略判断新鲜度。

四、解析、签名和旧字段要分别处理

解析成功只代表 DER 结构符合标准库能识别的格式。要确认 CRL 是由预期的签发者发布的,应取得对应的 *x509.Certificate,再调用 list.CheckSignatureFrom(issuer)。这个调用仍然不是完整的证书路径验证,它只处理 CRL 签名与签发者之间的关系。

目标使用方式不要混淆
从文件获得 DERpem.DecodePEM 外壳不是 ASN.1 内容
读取 CRL 字段ParseRevocationList解析成功不等于可信
读取被撤销证书RevokedCertificateEntries条目不是完整证书对象
确认签发者CheckSignatureFrom不替代整条证书链验证

如果需要兼容旧数据结构,可暂时读取已弃用的 RevokedCertificates 字段,但新代码应优先围绕 RevocationListEntry 组织结果。这样后续处理原因码和扩展字段时,边界更清楚。

相关问题

DER 文件还需要调用 pem.Decode 吗?

不需要。只有输入带 PEM 文本外壳时才先解码;已经是 DER 的字节可以直接传给 ParseRevocationList。

ParseRevocationList 成功后就能信任 CRL 吗?

不能。它只完成格式解析;需要确认来源时,还要用预期签发者调用 CheckSignatureFrom,并按业务要求处理 CRL 新鲜度。

为什么不继续使用 ParseDERCRL?

ParseDERCRL 已被官方标记为弃用,推荐迁移到 ParseRevocationList,以使用新的 RevocationList 与条目模型。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>