介绍
我有一个问题,即数字签名PDF/A-3A兼容的文件时出了问题。使用PDFBox(最新版本2.0.24),在Adobe Acrobat中最终获得无效签名,而使用iText7(最新版本)则获得有效签名。目标是获得PAdES LTV兼容的签名。
概览
我的流程如下(使用PDFBox和iText7):
- 打开PDF文档,创建用于签名的哈希(要签名的数据)
- 调用第三方服务以获取数字签名
- 在服务响应中,我还获取需要嵌入PDF中以获得LTV质量的OCSP和CRL内容
- 将签名嵌入PDF
- 将文档保存到内存中,然后重新打开以嵌入OCSP和CRL
- 嵌入OCSP和CRL项目,创建相应的DSS和VRI字典
- 将PDF保存到磁盘
对于PDFBox,签名代码在这里,OCSP/CRL嵌入代码在这里。对于iText7,签名和OCSP/CRL嵌入的代码在这里。
问题
现在,对于大多数PDF文件(包括多重签名文档),这都可以正常工作。问题出在一个特定的PDF上,该PDF以PDF/A兼容性级别3A创建。
使用PDFBox,如果我只嵌入签名并在Adobe Acrobat中打开文档,则签名有效。如果我还嵌入OCSP/CRL内容,则签名不再有效。Adobe Acrobat会报告:
签名无效:由于签署后已被更改或损坏,因此文档无法验证。
我还注意到,仅通过执行以下操作:
document.load(inputStream);
document.save(outputStream);
我破坏了签名。从我的测试来看,实际的嵌入并不是问题的真正原因,而只是因为在嵌入签名后重新打开PDF并将其保存回磁盘。
通过相同的过程(密钥,证书等),使用iText7最终在Adobe Acrobat中获得有效的LTV签名。
示例PDF
示例文档在这里。原始文档包含未签署的文档,然后有两个样本,一个用于PDFBox(在Adobe Acrobat中无效),另一个用于iText7(在Adobe Acrobat中有效)。
我到目前为止的研究表明,PDFBox在加载签名嵌入后的PDF时以某种方式破坏了元素的顺序。它提示这个问题与加载和保存文档有关,虽然对于所有其他PDF,我都执行相同的过程,Adobe Acrobat没有抱怨签名。
我还尝试了PDFBox 2.1.0-SNAPSHOT和3.0.0-SNAPSHOT,希望该问题与PDF中元素的顺序有关并已得到修复。但是,我得到了相同的结果。
后来编辑1
请参见下面的“后来编辑2”,此处的“后来编辑1”不是一个好主意!
根据下面@mkl的接受答案,问题出在原始PDF文件中,该文件将交叉引用表分成几个子部分而不是一个。这似乎是由首先生成PDF的服务使用的库(Aspose PDF for .NET,版本21.3或更早)引起的。
似乎可以与我的当前代码一起使用的一种解决方法是:
PDDocumentInformation info = pdDocument.getDocumentInformation();
if (info != null && StringUtils.containsIgnoreCase(info.getProducer(), "Aspose")) {
try {
pdDocument.save(inMemoryStream);
pdDocument.close();
pdDocument = PDDocument.load(inMemoryStream.toByteArray());
inMemoryStream.reset();
} catch (Exception e) {
如果我检测到文档的生成者是Aspose,我会将文档保存在内存中(通过PDFBox的pdDocument.save()),然后重新加载。这确保了交叉引用表在内存中正确地书写,并从那里进行签名和OCSP+CRL嵌入,从而在Adobe Acrobat中产生有效的签名。
后来编辑 2
谢谢@mkl和@TilmanHausherr,你们是对的。假设所有使用特定库生成的文档都必须自动归一化,这不是一个好主意,因为现有的签名将失效。最好的想法是将代码保持原样,并期望正确构造PDF时修复问题。