I have a digitally signed pdf with multiple signatures. Now I want to remove only one of the signatures. I am using itext for the same. The code is as follows:
PdfReader reader = new PdfReader(src_path);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest_path));
{
AcroFields.Item item = stamper.getAcroFields().getFieldItem(fieldname);
ClearSignatureDictionary(item.getMerged(0));
ClearSignatureDictionary(item.getWidget(0));
ClearSignatureDictionary(item.getValue(0));
}
}
private static void ClearSignatureDictionary(PdfDictionary dic)
{
dic.remove(PdfName.AP);
dic.remove(PdfName.AS);
dic.remove(PdfName.V);
dic.remove(PdfName.DV);
dic.remove(PdfName.SV);
dic.remove(PdfName.FF);
dic.put(PdfName.F, new PdfNumber(4));
}
But when I open the document with the removed signature, it gives me the following error on Acrobat reader
"At least one of the signatures is invalid"
You can not remove keys from a dictionary in a signed document, and expect the signatures to remain valid. You can only remove the last signature that was added. If a document was signed by multiple people, and you want to remove the first signature, all subsequent signatures will be broken.
This image explains why:
This image shows that every new digital signature keeps the original bytes intact. With every new signature new bytes are added. Rev1 represents the bytes of a document that has 1 digital signature. Rev2 represents the bytes of a document that has 2 digital signatures. The second digital signatures signs Rev1 completely. If you'd remove the first signature, the second signature would become invalid.
A digital signature is a special type of form field. With iText, you can get the names of the signature form fields of a PDF like this:
PdfReader reader = new PdfReader(path);
AcroFields fields = reader.getAcroFields();
ArrayList<String> names = fields.getSignatureNames();
You can only remove the signature that covers the whole document, for instance, if we have "sig1"
, "sig2"
, and "sig3"
(added in that order), only fields.signatureCoversWholeDocument("sig3")
will return true.
You can get the total number of revisions like this: fields.getTotalRevisions()
and a specific revision like this: fields.getRevision("sig1")
(provided that there's a signature field named "sig1"
).
Suppose that the image represents your document, and you have to remove 1 signature, then you can only remove the third signature by removing all the bytes that were added in revision 3 (Rev3). With iText, that means going back to revision 2 (Rev2). That revision was signed using the signature field sig2
.
You can extract this revision like this:
FileOutputStream os = new FileOutputStream("revision2.pdf");
byte bb[] = new byte[1028];
InputStream ip = fields.extractRevision("sig2");
int n = 0;
while ((n = ip.read(bb)) > 0)
os.write(bb, 0, n);
os.close();
ip.close();
The file revision2.pdf
will be the file signed by "sig1"
and "sig2"
without the bytes that were added when creating "sig3"
.