PDFBox - 打开并保存签名的 pdf 会使我的签名无效
PDFBox - opening and saving a signed pdf invalidates my signature
我正在尝试学习使用 Apache 的 pdfBox 来处理工作中的数字签名文档。在测试期间,我创建了一个完全空的 pdf 文档。
然后我使用带有证书的签名功能通过 Adobe reader 签署了文档。
我尝试在没有任何修改的情况下使用 pdfBox 打开、保存和关闭签名文件。但是,一旦我在 Adobe 中打开文件,文件就不再有效。
Adobe 告诉我:"There are errors in the formatting or information contained in this signature (support information: SigDict/Contents illegal data)"
由于我没有修改文件的内容,凭直觉应该没有任何问题,签名应该仍然有效,但事实并非如此,我不知道解决方案是什么(谷歌搜索没有结果)。
我如何创建文档:
@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();
}
然后我用 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
}
我真的不知道为什么这不起作用。任何帮助都会很棒!
编辑:
所以我做了一些挖掘,似乎更新现有的签名文档(添加注释或填写表格)尚未在 PDFBox 2.0.1 中实现,并且计划在 2.1 版中实现(但没有发布日期)已指定)。更多信息 here and here.
然而,似乎可以使用 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();
}
以上代码并没有使我的签名无效,但没有保存我添加的注释。
按照建议,我已将添加的注释、页面和注释列表的 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);
注释仍然没有保存,所以我显然遗漏了一些东西。
编辑 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();
}
当我在未签名的文档上使用它添加注释时,注释被添加并且可见。但是在签名文档上使用它时,注释不会出现。
我在 notepad++ 中打开了 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]
>>
但是当我在 adobe 中打开文档时它没有出现 reader。也许这与外观流有关而不是注释本身?
根据我从 Adobe 的一些文档中收集到的信息,签名中包含一个时间戳。我猜想通过保存文档(即使没有任何内容更改)会修改此时间戳值,从而使签名无效(这将使用原始时间戳创建)。
请注意,这不是权威性的 - 这只是我通过快速浏览 Adobe 关于此事的文档所能收集到的内容。
问题是使用 PDDocument.save() 会创建一个新文档,从而使签名无效。
使用PDDocument.saveIncremental(...) 不会使签名无效,但不会更新对文档的任何更改(例如注释或表单填写),它仅用于保存签名.
使用 PDFBox 2.0 尚不能更新带有注释或表单填写的签名 PDF 文档,但一旦 PDFBox 2.1 推出就应该可以实现。
然而,使用 IText 的 PDFStamper 解决了向签名文档添加注释而不会使签名无效的问题,如回答。
我正在尝试学习使用 Apache 的 pdfBox 来处理工作中的数字签名文档。在测试期间,我创建了一个完全空的 pdf 文档。
然后我使用带有证书的签名功能通过 Adobe reader 签署了文档。
我尝试在没有任何修改的情况下使用 pdfBox 打开、保存和关闭签名文件。但是,一旦我在 Adobe 中打开文件,文件就不再有效。
Adobe 告诉我:"There are errors in the formatting or information contained in this signature (support information: SigDict/Contents illegal data)"
由于我没有修改文件的内容,凭直觉应该没有任何问题,签名应该仍然有效,但事实并非如此,我不知道解决方案是什么(谷歌搜索没有结果)。
我如何创建文档:
@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();
}
然后我用 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
}
我真的不知道为什么这不起作用。任何帮助都会很棒!
编辑:
所以我做了一些挖掘,似乎更新现有的签名文档(添加注释或填写表格)尚未在 PDFBox 2.0.1 中实现,并且计划在 2.1 版中实现(但没有发布日期)已指定)。更多信息 here and here.
然而,似乎可以使用 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();
}
以上代码并没有使我的签名无效,但没有保存我添加的注释。
按照建议,我已将添加的注释、页面和注释列表的 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);
注释仍然没有保存,所以我显然遗漏了一些东西。
编辑 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();
}
当我在未签名的文档上使用它添加注释时,注释被添加并且可见。但是在签名文档上使用它时,注释不会出现。
我在 notepad++ 中打开了 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]
>>
但是当我在 adobe 中打开文档时它没有出现 reader。也许这与外观流有关而不是注释本身?
根据我从 Adobe 的一些文档中收集到的信息,签名中包含一个时间戳。我猜想通过保存文档(即使没有任何内容更改)会修改此时间戳值,从而使签名无效(这将使用原始时间戳创建)。
请注意,这不是权威性的 - 这只是我通过快速浏览 Adobe 关于此事的文档所能收集到的内容。
问题是使用 PDDocument.save() 会创建一个新文档,从而使签名无效。
使用PDDocument.saveIncremental(...) 不会使签名无效,但不会更新对文档的任何更改(例如注释或表单填写),它仅用于保存签名.
使用 PDFBox 2.0 尚不能更新带有注释或表单填写的签名 PDF 文档,但一旦 PDFBox 2.1 推出就应该可以实现。
然而,使用 IText 的 PDFStamper 解决了向签名文档添加注释而不会使签名无效的问题,如回答