PDF/A-3A文档的PAdES LTV签名生成了无效的签名。

5

介绍

我有一个问题,即数字签名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时修复问题。


看起来您正在使用Aspose.PDF for .NET生成/签署PDF文档。请确保使用最新版本的API,即21.8,如果仍然收到有问题的PDF,请在我们的官方Aspose.PDF支持论坛(https://forum.aspose.com/c/pdf/10)中分享完整的细节和代码片段,我们将相应地进行调查和解决问题。请注意,我们的支持论坛是报告和跟踪此类问题的正确位置。我是Aspose的开发者大使Asad Ali。 - Asad Ali
@AsadAli 更重要的是,OP似乎已经被给予了一个使用Aspose.PDF生成的PDF作为基于iText和PDFBox的签名方法的测试文档。特别是他没有使用Aspose.PDF进行签名,因此他不可能意识到在意识到问题实际上是由于他的测试文档而不是他的代码中的错误之前,寻求官方Aspose.PDF支持论坛的帮助是首选选项。 - mkl
你最后的编辑有点违背了给现有文件签名的整个想法。(如果你的文件已经有签名,那么保存它会使该签名无效) - Tilman Hausherr
关于您的第二次编辑 - 您可能需要考虑更改iText代码,以便在签署增量更新时不破坏先前的签名。或者首先检查PDF是否包含签名,在这种情况下使用追加模式。 - mkl
1个回答

6
问题是由原始PDF中的错误引起的。您的PDFBox代码以追加模式(即增量更新)进行签名,因此该错误也存在于已签名版本中。您的iText代码不使用追加模式进行签名,而是重新编写整个PDF;在这样做时,它不会像原始PDF的生产者那样出现相同的错误,因此错误不再存在于已签名版本中。Adobe Acrobat在验证带有更新的签名时对此类问题非常敏感。
错误
初始修订版的交叉引用表不能分成单独的子部分,但在您的原始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字典,它是由"Aspose.PDF for .NET 21.3.0"生成的。较早版本的Aspose.PDF已知会创建此类错误的交叉引用表(请参见上面引用的first answer referenced above中的“破坏PDF的PDF处理器”部分)。显然,Aspose尚未彻底解决这个问题。

非常感谢@mkl的回答!我现在正在与制作这些文档的团队一起解决这个问题,看看他们是否可以升级他们的Aspose PDF库。然而,我仍然想知道为什么如果我不包括OCSP和CRL,我在Acrobat中会得到一个有效的签名?在上面的PDF示例列表中,我包括了一个只有签名的PDF,没有添加DSS和VRI字典。那个签名在Adobe Acrobat中被标记为有效。这难道不是在暗示PDFBox在加载文档并将其保存回磁盘时存在某些问题吗? - BogdanM
1
没事了,我重新阅读了那部分内容,“除了在验证带有集成签名和已签署修订版本之后的增量更新文档时”。谢谢,现在很清楚了!我将此答案标记为已接受。 - BogdanM
@BogdanM,我今天看到了你针对这个问题的提交。请注意,A 并非所有 Aspose PDF 都会出现此问题,但是 B 其他 Aspose PDF 可能已经包含签名。因此,这种解决方法可能会更改比必要更多的 PDF,并且甚至可能损坏其中的现有签名。因此,这种解决方法应该至少是可选的。 - mkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接