You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java iText库获取多签名PDF中各签名的吊销信息

Java环境基于iText提取多签名PDF各签名吊销信息实现方案

前置依赖准备

使用iText 7.x版本实现(iText 5已停止维护,签名相关API对PAdES标准、DSS存储的支持存在缺失),需要引入的核心依赖包:

  • itext7-core:iText核心能力包,包含PDF解析、签名处理模块
  • bouncycastle-jdk15on、bouncycastle-pkix:提供PKI体系下CRL、OCSP、证书解析的底层支撑

代码运行前必须先注册BouncyCastle安全提供者,否则会出现安全算法找不到的异常:

Security.addProvider(new BouncyCastleProvider());

分步实现逻辑

1. 加载PDF并提取所有签名字段

已签署PDF的所有签名都存储在AcroForm的签名字段中,既包含用户添加的批准签名,也包含文档级的认证签名,遍历的时候不要做字段过滤遗漏:

// 加载目标PDF文档
PdfDocument pdfDoc = new PdfDocument(new PdfReader("target_signed.pdf"));
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDoc, false);
Map<String, PdfFormField> formFields = acroForm.getFormFields();

遍历所有字段,过滤出签名字段后逐个处理:

for (PdfFormField field : formFields.values()) {
    if (!field.isSignatureField()) {
        continue;
    }
    PdfSignature signature = field.getValueAsSignature();
    PdfPKCS7 pkcs7 = signature.getSignatureContainer().getPdfPKCS7();
    X509Certificate signCert = (X509Certificate) pkcs7.getSigningCertificate();
    System.out.printf("=== 处理签名者:%s 的吊销信息 ===%n", signCert.getSubjectDN().getName());
    // 后续提取吊销信息的逻辑写在这里
}

2. 提取签名容器内嵌入的吊销信息

签名生成时,签名工具通常会把对应的CRL(证书吊销列表)、OCSP(在线证书状态查询)响应直接嵌入PKCS7签名容器中,这部分数据可以直接通过iText封装的PdfPKCS7对象获取:

// 提取嵌入的CRL列表
Collection<CRL> embeddedCrls = pkcs7.getCRLs();
for (CRL crl : embeddedCrls) {
    X509CRL x509Crl = (X509CRL) crl;
    Set<? extends X509CRLEntry> revokedEntries = x509Crl.getRevokedCertificates();
    System.out.printf("签名容器内CRL:颁发者=%s,更新时间=%s,吊销条目数=%d%n",
            x509Crl.getIssuerDN().getName(),
            x509Crl.getThisUpdate(),
            revokedEntries == null ? 0 : revokedEntries.size()
    );
}

// 提取嵌入的OCSP响应
Collection<byte[]> embeddedOcspBytes = pkcs7.getOcspResponses();
for (byte[] ocspRespByte : embeddedOcspBytes) {
    OCSPResp ocspResp = new OCSPResp(ocspRespByte);
    BasicOCSPResp basicResp = (BasicOCSPResp) ocspResp.getResponseObject();
    System.out.printf("签名容器内OCSP:响应生成时间=%s,状态条目数=%d%n",
            basicResp.getProducedAt(),
            basicResp.getResponses().length
    );
}

3. 提取文档DSS存储区的吊销信息

符合PAdES标准的签名会把吊销信息统一存到PDF根节点的DSS(文档安全存储)字典中,按签名的哈希值建立VRI(签名验证相关信息)索引,这部分数据不会存在PKCS7容器里,必须单独读取:

PdfDictionary catalogDict = pdfDoc.getCatalog().getPdfObject();
PdfDictionary dssDict = catalogDict.getAsDictionary(PdfName.DSS);
if (dssDict != null) {
    PdfDictionary vriRoot = dssDict.getAsDictionary(PdfName.VRI);
    if (vriRoot != null) {
        // 计算当前签名内容的SHA1哈希,作为VRI条目的查找键
        byte[] sigContent = signature.getContents().getValueBytes();
        byte[] sigHash = DigestAlgorithms.digest(sigContent, DigestAlgorithm.SHA1);
        String vriKey = Hex.toHexString(sigHash).toUpperCase();
        PdfDictionary sigVri = vriRoot.getAsDictionary(new PdfName(vriKey));
        if (sigVri != null) {
            // 读取VRI中存储的CRL
            PdfArray crlArr = sigVri.getAsArray(PdfName.CRL);
            if (crlArr != null) {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                for (int i = 0; i < crlArr.size(); i++) {
                    PdfStream crlStream = crlArr.getAsStream(i);
                    X509CRL vriCrl = (X509CRL) cf.generateCRL(new ByteArrayInputStream(crlStream.getBytes()));
                    System.out.printf("DSS存储CRL:颁发者=%s,更新时间=%s%n",
                            vriCrl.getIssuerDN().getName(), vriCrl.getThisUpdate());
                }
            }
            // 读取VRI中存储的OCSP响应
            PdfArray ocspArr = sigVri.getAsArray(PdfName.OCSP);
            if (ocspArr != null) {
                for (int i = 0; i < ocspArr.size(); i++) {
                    PdfStream ocspStream = ocspArr.getAsStream(i);
                    OCSPResp vriOcsp = new OCSPResp(ocspStream.getBytes());
                    BasicOCSPResp vriBasicOcsp = (BasicOCSPResp) vriOcsp.getResponseObject();
                    System.out.printf("DSS存储OCSP:响应生成时间=%s%n", vriBasicOcsp.getProducedAt());
                }
            }
        }
    }
}

注意事项

  • 不要尝试手动解析ASN.1结构提取数据:iText已经对不同版本、不同标准的签名结构做了兼容,手动解析很容易出现不同签名工具生成的PDF适配问题。
  • 吊销信息可能为空:如果签名生成时没有嵌入CRL/OCSP数据,也没有写入DSS存储区,提取结果就是空集合,这属于签名本身的配置问题,不是代码逻辑错误。
  • 多签名场景下每个签名的VRI是独立存储的,后添加的签名不会修改之前签名对应的吊销信息,遍历处理时不需要做额外的签名覆盖范围判断。
  • 处理完文档后记得关闭PdfDocument释放文件句柄。

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:12:31