禁用在线获取时,SignatureValidator为何无法使用DSS中的OCSP/CRL?
问题描述
我基于iText 9.0构建了以下PDF归档验证工作流:
- 使用iText为PDF添加时间戳
- 将长期验证(LTV)信息嵌入文档安全存储(DSS)
- 通过
SignatureValidator验证文档是否符合归档标准
为实现离线验证,我配置SignatureValidator禁用OCSP/CRL的在线获取,但验证失败;仅启用在线获取时才能成功。调试发现SignatureValidator未使用嵌入在DSS中的OCSP/CRL数据。
复现代码如下:
// 1) 为PDF添加时间戳 var inputPdfReader = new PdfReader("not_signed.pdf"); var outputStream = new FileStream("signed.pdf", FileMode.Create); var tsaClient = new TSAClientBouncyCastle("https://freetsa.org/tsr", null, null, 8192, "SHA-256"); var stampingProperties = new StampingProperties(); PdfSigner pdfSigner = new PdfSigner(inputPdfReader, outputStream, stampingProperties); pdfSigner.Timestamp(tsaClient, null); outputStream.Close(); // 2) 嵌入LTV信息到DSS var signedReader = new PdfReader("signed.pdf"); var ltvWriter = new PdfWriter("signed_ltv.pdf"); var ltvDocument = new PdfDocument(signedReader, ltvWriter, new StampingProperties().UseAppendMode()); LtvVerification v = new LtvVerification(ltvDocument); SignatureUtil signatureUtil = new SignatureUtil(ltvDocument); var names = signatureUtil.GetSignatureNames(); var pkcs7 = signatureUtil.ReadSignatureData(names.Single()); Debug.Assert(pkcs7.IsTsp()); v.AddVerification(names[0], (IOcspClient)new OcspClientBouncyCastle(), new CrlClientOnline(), LtvVerification.CertificateOption.WHOLE_CHAIN, LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO); v.Merge(); ltvDocument.Close(); // 3) 验证LTV信息(禁用在线获取时失败) var validationProperties = new SignatureValidationProperties(); // 注释此行允许在线获取则验证成功,但需要完全离线验证 validationProperties.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); // iText 9.0 bug修复:处理缺失的必要扩展 List<CertificateExtension> certIssuerRequiredExtensions = new(); certIssuerRequiredExtensions.Add(new KeyUsageExtension(KeyUsage.KEY_CERT_SIGN)); certIssuerRequiredExtensions.Add(new DynamicBasicConstraintsExtension()); validationProperties.SetRequiredExtensions(CertificateSources.Of(CertificateSource.CERT_ISSUER), [new KeyUsageExtension(KeyUsage.KEY_CERT_SIGN), new MyDynamicBasicConstraintsExtension()]); var ks = new List<IX509Certificate>(); using (var stream = new FileStream("freetsa_root.cer", FileMode.Open)) { ks.Add(new X509CertificateBC(new X509CertificateParser().ReadCertificate(stream))); } using (var stream = new FileStream("freetsa_root2.cer", FileMode.Open)) { ks.Add(new X509CertificateBC(new X509CertificateParser().ReadCertificate(stream))); } var validatorChainBuilder = new ValidatorChainBuilder() .WithSignatureValidationProperties(validationProperties) .WithTrustedCertificates(ks); ltvDocument = new PdfDocument(new PdfReader("signed_ltv.pdf")); var validator = validatorChainBuilder.BuildSignatureValidator(ltvDocument); var validationReport = validator.ValidateSignatures(); var validationFailures = validationReport.GetFailures();
解决思路与方法
1. 确认DSS中已正确嵌入吊销数据
时间戳签名的LTV处理逻辑与普通签名略有差异,需确保LtvVerification的配置正确写入了OCSP/CRL数据到DSS:
- 检查
names[0]是否为正确的时间戳签名名称,可通过signatureUtil.GetSignatureNames()输出确认 - 调整
AddVerification的参数:对于时间戳,可尝试将CertificateOption改为SIGNING_CERTIFICATE,避免不必要的证书链处理 - 使用PDF查看工具(如iText RUPS)打开
signed_ltv.pdf,确认DSS字典中包含OCSPs和CRLs条目
2. 显式配置SignatureValidator使用嵌入的DSS数据
iText 9的SignatureValidator默认不会自动优先读取DSS中的吊销数据,需手动提取并注入到验证链中:
// 在构建validator前,从DSS提取嵌入的吊销数据 DssParser dssParser = new DssParser(ltvDocument); DssInfo dssInfo = dssParser.Parse(); List<IRevocationData> embeddedRevocationData = new List<IRevocationData>(); embeddedRevocationData.AddRange(dssInfo.GetOcsps().Cast<IRevocationData>()); embeddedRevocationData.AddRange(dssInfo.GetCrls().Cast<IRevocationData>()); // 将嵌入数据添加到验证链构建器 var validatorChainBuilder = new ValidatorChainBuilder() .WithSignatureValidationProperties(validationProperties) .WithTrustedCertificates(ks) .WithAdditionalRevocationData(embeddedRevocationData);
3. 调整验证属性配置
确保validationProperties允许从文档嵌入来源获取吊销数据:
- 无需额外禁用文档内吊销数据的读取,默认配置下
SignatureValidator会优先使用嵌入数据,但需显式注入(如步骤2) - 确认
MyDynamicBasicConstraintsExtension的实现正确,避免因证书扩展验证失败导致整体验证不通过
4. 验证时间戳签名的特殊逻辑
时间戳签名的验证依赖TSA证书链,需确保:
- 信任库中包含freetsa.org的完整根证书链
- 时间戳的签名数据被正确识别为TSP(代码中已通过
Debug.Assert(pkcs7.IsTsp())确认,此部分无问题)
内容的提问来源于stack exchange,提问作者craffael
相关产品推荐
相关产品推荐

