PDFBox 1.8.10:填写并签署 PDF 会产生无效签名

PDFBox 1.8.10: Fill and Sign PDF produces invalid signatures

我在 PDF 文档中(以编程方式)填写表格 (AcroPdf),然后在文档上签名。我从 doc.pdf 开始,创建 doc_filled.pdf,使用 PDFBox 的 setFields.java 示例。然后我签名 doc_filled.pdf,创建 doc?filled_signed.pdf,使用一些代码,基于签名示例并在 Acrobat Reader 中打开 pdf。输入的Field数据可见,签名面板告诉我

"There are errors in the formatting or information contained in this signature (The signature byte array is invalid)"

到目前为止,我知道:

足以破解后来应用的签名代码。

另一方面,如果我采用相同的 doc.pdf,在 Adob​​e 中手动输入字段值,签名代码会生成有效签名。

我做错了什么?

更新:

@mkl 要求我提供文件,我说的是(我目前没有足够的声誉,以 post 所有文件作为链接,对于给您带来的不便,我们深表歉意):

最后一个是通过一次性签署和填写文档创建的,使用

    doc.saveIncremental(); 

正如我已经在评论中写的,一些

    setNeedToBeUpdate(true);

不过好像不见了。 参考@mkl的第二条评论,我发现了这个 所以问题:,这也涵盖了一些未显示的输入文本。我第一次尝试,申请

    setBoolean(COSName.getPDFName("NeedAppearances"), true); 

到字段和表单的字典,然后显示字段上下文,但最后没有添加签名。我仍然需要进一步研究。

更新: 故事在这里继续:PDFBox 1.8.10: Fill and Sign Document, Filling again fails

OP 的原始问题的原因,即在使用 PDFBox 加载他的 PDF(用于填写表格)然后保存后,这个新的 PDF 无法使用 PDFBox 签名代码成功签名,已经在 中的详细信息,简而言之:

  • 定期保存文档时,PDFBox 使用交叉引用 table.

    • 如果要定期保存的文档是从具有交叉引用流的 PDF 加载的,则交叉引用流字典的所有条目都保存在尾部字典中。
  • 在应用签名的过程中保存文档时,PDFBox创建增量更新;由于此类增量更新要求更新使用与原始修订相同类型的交叉引用,PDFBox 在这种情况下尝试使用相同的技术。

    • 为了识别最初使用的技术,PDFBox 查看其文档表示中字典的 Type 条目,拖车或交叉引用流字典已加载到其中:如果有Type 条目值为 XRef(为交叉引用流指定),则假定为流,否则为 table.

因此,对于具有交叉引用流的 OP 原始 PDF doc.pdf

  • 加载填表后文档是定时保存的,即使用交叉引用table,但是之前的所有交叉引用流条目,其中Type,复制到预告片中。 (doc_filled.pdf)

  • 使用交叉引用 table 加载此保存的 PDF 进行签名后,使用增量更新再次保存。 PDFBox 假定(由于 Type 尾部条目)现有文件具有交叉引用流,因此在增量更新结束时也使用交叉引用流。 (doc_filled_signed.pdf)

  • 因此,最终填写并签名的PDF有两个修改,内部一个是交叉引用table,外部一个是交叉引用流。

  • 由于这是无效的,Adobe Reader 在加载 PDF 后,在其内部文档表示中修复了此问题。修复会更改文档字节。于是,AdobeReader眼里的签名就坏了

  • 大多数其他签名验证器不会尝试进行此类修复,而是按原样检查文档的签名。他们成功验证了签名。

也提供了一些解决方法:

  • A:加载 PDF 以填写表格后,在定期保存之前从预告片中删除 Type 条目。如果对该文件应用签名,PDFBox 将假定交叉引用 table(因为误导性 Type 条目不存在。因此,签名增量更新将有效。

  • B:也使用增量更新来保存表单填写更改,在单独的 运行 或与签名相同的 运行 中。这也会导致有效的增量更新。

通常我会建议后一种选择,因为如果 PDFBox 保存例程相互兼容,前一种选择可能会失效。

不幸的是,后一个选项需要将添加和更改的对象标记为已更新,包括来自文档目录的路径。如果这不可能或至少太麻烦, 第一个选项可能更可取。


在手头的案例中,OP 尝试了后一种选择 (doc_filled_and_signed.pdf):

At the Moment the text box's content is only visible, when the text box is selected (with Acrobat reader and Preview the same behaviour). I flag the PDField, all of its parents, the AcroForm, the Catalog as well as the page where it is displayed.

他将更改的字段标记为已更新,但未将关联的外观流标记为 已更新,该外观流是在设置表单字段值时由 PDFBox 自动生成的。

因此,在结果 PDF 文件中,该字段具有新值,但具有旧的、空的外观流。只有单击该字段时,Adobe Reader 会根据编辑值创建新外观。

因此,OP 还必须标记新的正常外观流(表单字段字典包含一个条目 AP 引用一个字典,其中 N 引用正常外观流)。或者(如果查找更改或添加的条目变得太麻烦)他可能会尝试其他选项。