PDFBox - 打开并保存签名的 pdf 会使我的签名无效

Sub*_*ies 5 java pdf adobe digital-signature pdfbox

我正在尝试学习使用 Apache 的 pdfBox 来处理工作中的数字签名文档。在测试过程中,我创建了一个完全空的 pdf 文档。

然后我通过 Adob​​e reader 使用带有证书的签名功能对文档进行了签名。

我尝试使用 pdfBox 打开、保存和关闭签名文件,而不进行任何修改。但是,一旦我在 Adob​​e 中打开该文件,这些文件就不再有效。

Adobe 告诉我:“此签名中包含的格式或信息存在错误(支持信息:SigDict/Contents 非法数据)”

由于我没有修改文件的内容,直观上不应该有任何问题,签名应该仍然有效,但事实并非如此,我不知道解决方案是什么(谷歌搜索没有结果)。

我如何创建文档:

@Test
public void createEmptyPDF() throws IOException {
    String path = "path to file";
    PDDocument document = new PDDocument();
    PDPage page = new PDPage();
    document.addPage(page);
    document.save(path);
    document.close();
}
Run Code Online (Sandbox Code Playgroud)

然后我用 adobe 签名并通过以下方式传递:

 @Test
public void copySignedDocument() throws IOException {
    String path = "path to file";
    File file = new File(path);
    PDDocument document = PDDocument.load(file);
    document.save(file);
    document.close();

    //just opening and saving the file invalidates the signatures
}
Run Code Online (Sandbox Code Playgroud)

我真的不知道为什么这行不通。任何帮助都会很棒!

编辑:

所以我做了一些挖掘,似乎更新现有的签名文档(添加注释或填写表单)尚未在 PDFBox 2.0.1 中实现,并计划在版本 2.1 中出现(但尚未指定发布日期)。更多信息请参见此处此处

然而,似乎可以使用 IText 在签名文档上添加注释,而无需使用 PDFStamper 使签名无效,来自这个问题

编辑 2:将图章添加到文档并增量保存的代码:

 @Test
public void stampSignedDocument() throws IOException {
    File file = new File("path to file");
    PDDocument document = PDDocument.load(file);
    File image = new File("path to image to be added to annotation");
    PDPage page = document.getPage(0);
    List<PDAnnotation> annotations = page.getAnnotations();
    PDImageXObject ximage = PDImageXObject.createFromFileByContent(image, document);

    //stamp
    PDAnnotationRubberStamp stamp = new PDAnnotationRubberStamp();
    stamp.setName("testing rubber stamp");
    stamp.setContents("this is a test");
    stamp.setLocked(true);
    stamp.setReadOnly(true);
    stamp.setPrinted(true);

    PDRectangle rectangle = createRectangle(100, 100, 100, 100, 100, 100);
    PDFormXObject form = new PDFormXObject(document);
    form.setResources(new PDResources());
    form.setBBox(rectangle);
    form.setFormType(1);

    form.getResources().add(ximage);
    PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
    PDAppearanceDictionary appearance = new PDAppearanceDictionary(new COSDictionary());
    appearance.setNormalAppearance(appearanceStream);
    stamp.setAppearance(appearance);
    stamp.setRectangle(rectangle);
    PDPageContentStream stream = new PDPageContentStream(document, appearanceStream);
    Matrix matrix = new Matrix(100, 0, 0, 100, 100, 100);
    stream.drawImage(ximage, matrix);
    stream.close();
    //close and save   
    annotations.add(stamp);
    page.getCOSObject().setNeedToBeUpdated(true);
    OutputStream os = new FileOutputStream(file);
    document.saveIncremental(os);
    document.close();
    os.close();
}
Run Code Online (Sandbox Code Playgroud)

上面的代码不会使我的签名无效,但不会保存我添加的注释。

按照建议,我已将添加的注释、页面和注释列表的 NeedToBeUpdated 标志设置为 true (我希望我正确地完成了最后一项):

    stamp.getCOSObject().setNeedToBeUpdated(true);
    COSArrayList<PDAnnotation> list = (COSArrayList<PDAnnotation>) annotations;
    COSArrayList.converterToCOSArray(list).setNeedToBeUpdated(true);
    page.getCOSObject().setNeedToBeUpdated(true);
    document.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
Run Code Online (Sandbox Code Playgroud)

注释仍未保存,所以我显然遗漏了一些东西。

编辑3:

这是我当前添加注释的方法:

    @Test
public void stampSignedDocument() throws IOException {
    File file = new File(
            "E:/projects/eSign/g2digitalsignature/G2DigitalSignatureParent/G2DigitalSignatureTest/src/test/resources/pdfBoxTest/empty.pdf");
    PDDocument document = PDDocument.load(file);
    File image = new File(
            "E:/projects/eSign/g2digitalsignature/G2DigitalSignatureParent/G2DigitalSignatureTest/src/test/resources/pdfBoxTest/digitalSign.png");
    PDPage page = document.getPage(0);
    List<PDAnnotation> annotations = page.getAnnotations();
    PDImageXObject ximage = PDImageXObject.createFromFileByContent(image, document);

    //stamp
    PDAnnotationRubberStamp stamp = new PDAnnotationRubberStamp();
    stamp.setName("testing rubber stamp");
    stamp.setContents("this is a test");
    stamp.setLocked(true);
    stamp.setReadOnly(true);
    stamp.setPrinted(true);

    PDRectangle rectangle = createRectangle(100, 100, 100, 100, 100, 100);
    PDFormXObject form = new PDFormXObject(document);
    form.setResources(new PDResources());
    form.setBBox(rectangle);
    form.setFormType(1);

    form.getResources().getCOSObject().setNeedToBeUpdated(true);
    form.getResources().add(ximage);
    PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
    PDAppearanceDictionary appearance = new PDAppearanceDictionary(new COSDictionary());
    appearance.setNormalAppearance(appearanceStream);
    stamp.setAppearance(appearance);
    stamp.setRectangle(rectangle);
    PDPageContentStream stream = new PDPageContentStream(document, appearanceStream);
    Matrix matrix = new Matrix(100, 0, 0, 100, 100, 100);
    stream.drawImage(ximage, matrix);
    stream.close();
    //close and save   
    annotations.add(stamp);

    appearanceStream.getCOSObject().setNeedToBeUpdated(true);
    appearance.getCOSObject().setNeedToBeUpdated(true);
    rectangle.getCOSArray().setNeedToBeUpdated(true);
    stamp.getCOSObject().setNeedToBeUpdated(true);
    form.getCOSObject().setNeedToBeUpdated(true);
    COSArrayList<PDAnnotation> list = (COSArrayList<PDAnnotation>) annotations;
    COSArrayList.converterToCOSArray(list).setNeedToBeUpdated(true);
    document.getPages().getCOSObject().setNeedToBeUpdated(true);
    page.getCOSObject().setNeedToBeUpdated(true);
    document.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);

    OutputStream os = new FileOutputStream(file);
    document.saveIncremental(os);
    document.close();
    os.close();

}
Run Code Online (Sandbox Code Playgroud)

当我在未签名的文档上使用它添加注释时,注释将被添加并可见。但是,当在签名文档上使用它时,注释不会出现。

我已经在记事本++中打开了pdf文件,发现注释似乎已经添加,因为我发现了这个以及与注释相关的其余代码:

<<
/Type /Annot
/Subtype /Stamp
/Name /testing#20rubber#20stamp
/Contents (this is a test)
/F 196
/AP 29 0 R
/Rect [100.0 100.0 200.0 200.0]
>>
Run Code Online (Sandbox Code Playgroud)

但是,当我在 adobe reader 中打开该文档时,它不会出现。也许这与外观流的关系大于注释本身?

Sub*_*ies 4

问题是使用 PDDocument.save() 创建一个新文档,从而使签名无效。

使用 PDDocument.saveIncremental(...) 不会使签名无效,但它不会更新对文档的任何更改(例如注释或表单填写),它仅用于保存签名。

使用 PDFBox 2.0 尚无法使用注释或表单填写来更新已签名的 PDF 文档,但一旦 PDFBox 2.1 推出,就应该可以实现。

有关问题的信息:此处此处

然而,使用 IText 的 PDFStamper 可以解决向签名文档添加注释的问题,而不会使签名无效,如此处的答案

  • *“使用 PDFBox 2.0 尚无法通过注释或表单填写来更新已签名的 PDF 文档”* - 更重要的是:您必须将所有更改或添加的对象标记为“NeedToBeUpdate”,包括文档目录中的路径。目前您必须手动执行此操作,而当前的问题针对的是 2.1,重点是自动化操作。 (3认同)
  • *“您愿意对此进行扩展吗?”* - 据我所知,PDFBox(增量保存时)从 PDF 目录字典开始,并使用 `NeedToBeUpdate` 查找引用的对象。它将这些对象添加到更新部分,然后在这些对象中使​​用“NeedToBeUpdate”等查找对其他对象的引用。因此,您需要从目录到您添加/更改的对象的引用链。对于注释,您可能会选择 **目录** -&gt; **页面** [-&gt; **页面** ...]-&gt; **页面** -&gt; **注释**。 (2认同)