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

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

“此签名中包含的格式或信息存在错误(签名字节数组无效)”

到目前为止,我知道:

  • 单独应用的签名代码(即直接创建一些doc_signed.pdf)会创建一个有效的签名
  • “隐形签名”,可见签名和可见签名存在的问题被添加到现有签名字段中。
  • 问题甚至发生,如果我没有填写表格,但只打开并保存,即:

    PDDocument doc = PDDocument.load(new File("doc.pdf")); doc.save(new File("doc_filled.pdf")); doc.close(); 

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

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

我究竟做错了什么?

更新:

@mkl要求我提供文件,我正在谈论(我目前没有足够的声誉,将所有文件发布为链接,抱歉给您带来不便):

  • odc.pdf: https ://www.dropbox.com/s/ev8x9q48w5l0hof/doc.pdf ? dl = 0
  • doc_filled.pdf: https ://www.dropbox.com/s/fxn4gyneizs1zzb/doc_filled.pdf ? dl = 0
  • doc_filled_signed.pdf: https ://www.dropbox.com/s/xm846sj8f9kiga9/doc_filled_signed.pdf ? dl = 0
  • doc_filled_and_signed.pdf: https ://www.dropbox.com/s/5jftje6ke87jedr/doc_filled_and_signed.pdf ? dl = 0

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

  doc.saveIncremental(); 

正如我已经在评论中写的那样

  setNeedToBeUpdate(true); 

但似乎缺少了。 参考@mkl的第二条评论,我发现了这个问题: 使用PDFBOX生成的PDF中无法正确显示保存的文本字段值 ,这也包括一些未显示的输入文本。 申请时,我先试了一下

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

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

更新:故事在这里继续: PDFBox 1.8.10:填写并签署文档,再次填充失败

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

  • 定期保存文档时,PDFBox使用交叉引用表进行保存。

    • 如果定期保存的文档已从具有交叉引用流的PDF加载,则交叉引用流字典的所有条目都保存在预告字典中。
  • 在应用签名的过程中保存文档时,PDFBox会创建增量更新; 因为这样的增量更新要求更新使用与原始版本相同类型的交叉引用,在这种情况下,PDFBox会尝试使用相同的技术。

    • 为了识别最初使用的技术,PDFBox在其文档表示中查看字典的Type条目,其中已加载了预告片或交叉引用流字典:如果存在值为XRefType条目(为交叉引用流指定) ,假定流,否则为表。

因此,在OP的原始PDF doc.pdf的情况下,它具有交叉引用流:

  • 在加载和表单填写后,文档会定期保存,即使用交叉引用表,但所有以前的交叉引用流条目(其中包括Type )都将复制到预告片中。 ( doc_filled.pdf

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

  • 因此,最后填写的,然后签名的PDF有两个修订版,内部一个带有交叉引用表,外部带有交叉引用流。

  • 由于这是无效的,Adobe Reader在加载PDF时会在其内部文档表示中对其进行修复。 修复会更改文档字节。 因此,Adobe Reader眼中的签名被打破了。

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

上面提到的答案也提供了一些方法:

  • 答:在加载表格填写的PDF后,请在定期保存之前从预告片中删除“ 类型”条目。 如果对此文件应用了签名,则PDFBox将采用交叉引用表(因为误导性的类型条目不存在。因此,签名增量更新将是有效的。

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

通常我会提出后一种选择,因为如果PDFBox保存例程彼此兼容,前一选项可能会中断。

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


在手头的情况下,OP尝试了后一个选项( doc_filled_and_signed.pdf ):

只有当选中文本框时,文本框的内容才可见(使用Acrobat阅读器和预览相同的行为)。 我标记了PDField,其所有父项,AcroForm,目录以及显示它的页面。

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

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

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