I am trying to learn to use Apache's pdfBox to deal with digitaly signed documents for work. During testing, I created a completely empty pdf document.
I then signed the document through Adobe reader using the sign with certificate function.
I tried to open, save and close the signed file with pdfBox without any modifications. However once I open the file in Adobe the files are no longer valid.
Adobe tells me: "There are errors in the formatting or information contained in this signature (support information: SigDict/Contents illegal data)"
Since I have not modified the content of the file, intuitively there should not have been any problems and the signature should be still valid, however this is not the case and I don't know what the solutions are (googling yielded no results).
How I create the document:
@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();
}
I then sign it with adobe and pass it through this:
@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
}
I am truly at a loss as to why this does not work. Any help would be great!
EDIT:
So I did some digging around and it seems that updating an existing signed document (either adding annotations or filling forms) is not yet implemented in PDFBox 2.0.1 and is scheduled to come in versions 2.1 (however no release date has been specified). More information here and here.
However it seems possible to add annotations on signed documents with IText without invalidating the signature using PDFStamper, from this question
EDIT 2: Code to add a stamp to a document and save it incrementally:
@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();
}
The above code doesn't invalidate my signature but doesn't save the annotation that I have added.
As suggested I've set the NeedToBeUpdated flag to true for the added annotation, page and annotations list (I hope I did the last one correctly):
stamp.getCOSObject().setNeedToBeUpdated(true);
COSArrayList<PDAnnotation> list = (COSArrayList<PDAnnotation>) annotations;
COSArrayList.converterToCOSArray(list).setNeedToBeUpdated(true);
page.getCOSObject().setNeedToBeUpdated(true);
document.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
The annotation is still not saved so I'm obviously missing something.
EDIT 3:
This is my current method to add an annotation:
@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();
}
When I add an annotation using it on a non signed document, the annotation gets added and is visible. However when using it on a signed document, the annotation does not appear.
I have opened the pdf file in notepad++ and have found that the annotation seems to have been added since I found this as well as the rest of the code pertaining to the annotation:
<<
/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]
>>
However it does not appear when I open the document in adobe reader. Perhaps this has more to do with the appearance streams than the annotation itself?