I am trying to sign a PDF with 2 signature fields using the example code provided by PDFBox (https://svn.apache.org/repos/asf/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
). But the signed PDF shows There have been changes made to this document that invalidate the signature
.
I have uploaded my sample project to GitHub please find it here.
The project can be opened using IntelliJ or Eclipse.
The program argument should be set to the following to simulate the problem.
keystore/lawrence.p12 12345678 pdfs/Fillable-2.pdf images/image.jpg
Grateful if any PDFBox expert can help me. Thank you.
This answer to the question “Lock” dictionary in signature field is the reason of broken signature after signing already contains code for signing that respects the signature Lock dictionary and creates a matching FieldMDP transformations while signing.
As clarified in a comment, though, the OP wonders
is there any way to lock the corresponding textfield after signing
Thus, not only shall changes to protected form fields invalidate the signature in question but in the course of signing these protected fields shall themselves be locked.
Indeed, one can improve the code from the referenced answer to do that, too:
PDSignatureField signatureField = FIND_YOUR_SIGNATURE_FIELD_TO_SIGN;
PDSignature signature = new PDSignature();
signatureField.setValue(signature);
COSBase lock = signatureField.getCOSObject().getDictionaryObject(COS_NAME_LOCK);
if (lock instanceof COSDictionary)
{
COSDictionary lockDict = (COSDictionary) lock;
COSDictionary transformParams = new COSDictionary(lockDict);
transformParams.setItem(COSName.TYPE, COSName.getPDFName("TransformParams"));
transformParams.setItem(COSName.V, COSName.getPDFName("1.2"));
transformParams.setDirect(true);
COSDictionary sigRef = new COSDictionary();
sigRef.setItem(COSName.TYPE, COSName.getPDFName("SigRef"));
sigRef.setItem(COSName.getPDFName("TransformParams"), transformParams);
sigRef.setItem(COSName.getPDFName("TransformMethod"), COSName.getPDFName("FieldMDP"));
sigRef.setItem(COSName.getPDFName("Data"), document.getDocumentCatalog());
sigRef.setDirect(true);
COSArray referenceArray = new COSArray();
referenceArray.add(sigRef);
signature.getCOSObject().setItem(COSName.getPDFName("Reference"), referenceArray);
final Predicate<PDField> shallBeLocked;
final COSArray fields = lockDict.getCOSArray(COSName.FIELDS);
final List<String> fieldNames = fields == null ? Collections.emptyList() :
fields.toList().stream().filter(c -> (c instanceof COSString)).map(s -> ((COSString)s).getString()).collect(Collectors.toList());
final COSName action = lockDict.getCOSName(COSName.getPDFName("Action"));
if (action.equals(COSName.getPDFName("Include"))) {
shallBeLocked = f -> fieldNames.contains(f.getFullyQualifiedName());
} else if (action.equals(COSName.getPDFName("Exclude"))) {
shallBeLocked = f -> !fieldNames.contains(f.getFullyQualifiedName());
} else if (action.equals(COSName.getPDFName("All"))) {
shallBeLocked = f -> true;
} else { // unknown action, lock nothing
shallBeLocked = f -> false;
}
lockFields(document.getDocumentCatalog().getAcroForm().getFields(), shallBeLocked);
}
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("blablabla");
signature.setLocation("blablabla");
signature.setReason("blablabla");
signature.setSignDate(Calendar.getInstance());
document.addSignature(signature [, ...]);
(CreateSignature helper method signAndLockExistingFieldWithLock
)
with lockFields
implemented like this:
boolean lockFields(List<PDField> fields, Predicate<PDField> shallBeLocked) {
boolean isUpdated = false;
if (fields != null) {
for (PDField field : fields) {
boolean isUpdatedField = false;
if (shallBeLocked.test(field)) {
field.setFieldFlags(field.getFieldFlags() | 1);
if (field instanceof PDTerminalField) {
for (PDAnnotationWidget widget : ((PDTerminalField)field).getWidgets())
widget.setLocked(true);
}
isUpdatedField = true;
}
if (field instanceof PDNonTerminalField) {
if (lockFields(((PDNonTerminalField)field).getChildren(), shallBeLocked))
isUpdatedField = true;
}
if (isUpdatedField) {
field.getCOSObject().setNeedToBeUpdated(true);
isUpdated = true;
}
}
}
return isUpdated;
}
(CreateSignature helper method lockFields
)