简介
我有一个问题,数字签名的PDF文件已标记为PDF/A-3A兼容。使用PDFBox(最新版本,2.0.24),我最终会在Adobe Acrobat中得到一个无效的签名,而使用iText7(最新版),我会得到一个有效的签名。目标是获得符合PAdES LTV的签名。
概述
我的流程如下(使用PDFBox和iText7):
- 我打开PDF,创建用于签名的哈希(要签名的数据)
- 我打电话给第三方服务以取回数字签名
- 在服务响应中,我还获得了OCSP和CRL内容,这些内容需要嵌入到PDF中以获得LTV质量
- 我在PDF中嵌入签名
- 我将文档保存到内存中,然后重新打开它以嵌入OCSP和CRL
- 我嵌入OCSP和CRL项目,创建相应的DSS和VRI字典
- 我将PDF保存到磁盘
对于PDFBox,签名代码在这里,OCSP/CRL嵌入代码在这里。对于iText7,这里有用于签名和OCSP/CRL嵌入的代码。
问题
现在,这适用于大多数PDF文件,包括多签名文档。问题出在一个特定的PDF上,它被创建为PDF/A complat,级别为3A。
使用PDFBox,如果我只是嵌入签名并在Adobe Acrobet中打开文档,则签名是有效的。如果我还嵌入OCSP/CRL内容,则签名不再有效。Adobe Acrobat抱怨:
签名无效:文档自签名以来已被更改或损坏。
我还注意到,只需执行以下操作:
document.load(inputStream);
document.save(outputStream);
我打破了签名。根据我的测试,实际的嵌入并不是问题的真正原因,而是我在嵌入签名后重新打开PDF并将其保存回磁盘。
通过iText7使用相同的过程(密钥、证书等),我最终在Adobe Acrobat中获得了有效的LTV签名。
PDF示例
样本文件在这里。原件包含未签名的文档,然后有两个样本,一个用于PDFBox(在Adobe Acrobat中无效),另一个用于iText7(在Adobe Adobe Acrobat中有效)。
到目前为止,我的研究表明,在签名嵌入后加载PDF时,PDFBox以某种方式破坏了元素的顺序。它暗示了加载和保存文档的这个问题,尽管对于所有其他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和@TilmanHauser,你说得对。假设使用某个库生成的所有文档都必须自动规范化,这不是一个好主意,因为现有签名将无效。最后,更好的想法是保持代码的原样,并期望得到一个正确构建的PDF。修复创建它的位置的问题。
问题是由原始PDF中的错误引起的。您的PDFBox代码以附加模式(即增量更新)进行签名,因此签名版本中也存在错误。您的iText代码不以附加模式登录,而是重写整个PDF;在这样做的同时,它不会产生与原始PDF制作者相同的错误,因此该错误不再出现在签名版本中。在使用更新验证签名时,Adobe Acrobat对此类问题非常敏感
错误
PDF中初始修订的交叉参考表不得拆分为单独的子部分,但如果您的原始PDF已拆分:
0 75
0000000000 65535 f
0000000018 00000 n
...
0000313374 00000 n
0000313397 00000 n
76 20
0000313419 00000 n
0000313443 00000 n
...
0000846048 00000 n
0000846175 00000 n
类似的案例已经在这个答案、这个答案、这一答案和其他地方进行了讨论;您还可以在这些答案中找到一些规范参考。
通常这种情况不会被注意到,Adobe Acrobat在PDF中遇到小问题时通常会非常松懈。
通常,也就是说,除了在签名修订版之后验证具有集成签名和增量更新的文档之外,在这种情况下,Adobe Acrobat通常认为这些问题是可疑的,并且无法验证签名,即使在签名修订版本之后验证没有增量更新的同一PDF时它也不会抱怨。
您正处于这种危急的情况下,您的最终文档在签署修订后包含一个增量更新,一个包含验证相关信息的更新。
谁造成了错误
根据您的原始PDF的Info字典,它是由";用于.NET 21.3.0的Aspose.PDF";。已知早期版本的Aspose.PDF会创建这种错误的交叉引用表(参见上文引用的第一个答案的"损坏PDF的PDF处理器"一节)。显然,Aspose还没有抽出时间来彻底解决这个问题。