“AT least one signature is invalid” error message

2019-09-13 12:35发布

问题:

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"

回答1:

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".