'Pdf signature invalidates existing signature in Acrobat Reader

I'm using iText 7.1.15 and SignDeferred to apply signatures to pdf documents. SignDeferred is required since the signature is created PKCS11 hardware token (usb key).

When i sign a "regular" pdf, e.g. created via word, i can apply multiple signatures and all signatures are shown as valid in the adobe acrobat reader.

If the pdf was created by combining multiple pdf documents with adobe DC, the first signature is valid but becomes invalid as soon as the seconds signature is applied.

Document in Adobe reader after the first signature is applied: enter image description here

Document in Adobe reader after the second signature is applied: enter image description here

The signatures of the same document are shown as valid in foxit reader.

I've found a similar issue on stackoverflow (multiple signatures invalidate first signature in iTextSharp pdf signing), but it was using iText 5 and i'm not sure it is the same problem.

Question: What can i do in order to keep both signatures valid in the Acrobat Reader?

Unsigned Pdf document on which the first signature becomes invalid: https://github.com/suntsu42/iTextDemoInvalidSecondSignature/blob/master/test.pdf

Twice signed document which is invalid: https://github.com/suntsu42/iTextDemoInvalidSecondSignature/blob/master/InvalidDocumentSignedTwice.pdf

Code used for signing

  //Step #1 >> prepare pdf for signing (Allocate space for the signature and calculate hash)
            using (MemoryStream input = new MemoryStream(pdfToSign))
            {
                using (var reader = new PdfReader(input))
                {
                    StampingProperties sp = new StampingProperties();
                    sp.UseAppendMode();

                    using (MemoryStream baos = new MemoryStream())
                    {
                        var signer = new PdfSigner(reader, baos, sp);
             

                        //Has to be NOT_CERTIFIED since otherwiese a pdf cannot be signed multiple times
                        signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);

                        if (visualRepresentation != null)
                        {
                            try
                            {
                                PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
                                base.SetPdfSignatureAppearance(appearance, visualRepresentation);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Unable to set provided signature image", ex);
                            }
                        }

                        //Make the SignatureAttributeName unique
                        SignatureAttributeName = $"SignatureAttributeName_{DateTime.Now:yyyyMMddTHHmmss}";
                        signer.SetFieldName(SignatureAttributeName);
                        DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);

                        signer.SignExternalContainer(external, EstimateSize);
                        hash = external.GetDocBytesHash();
                        tmpPdf = baos.ToArray();
                    }
                }

                //Step #2 >> Create the signature based on the document hash
                // This is the part which accesses the HSM via PCKS11
                byte[] signature = null;
                if (LocalSigningCertificate == null)
                {
                    signature = CreatePKCS7SignatureViaPKCS11(hash, pin);
                }
                else
                {
                    signature = CreatePKCS7SignatureViaX509Certificate(hash);
                }

                //Step #3 >> Apply the signature to the document
                ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signature);
                using (MemoryStream preparedPdfStream = new MemoryStream(tmpPdf))
                {
                    using (var pdfReader = new PdfReader(preparedPdfStream))
                    {
                        using (PdfDocument docToSign = new PdfDocument(pdfReader))
                        {
                            using (MemoryStream outStream = new MemoryStream())
                            {
                                PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
                                return outStream.ToArray();
                            }
                        }
                    }
                }

            }

Sample project

I've created a working sample project which uses a local certificate for signing. I also did update iText to version 7.2 but with the same result. It also contains the document which cannot be signed twice (test.pdf) https://github.com/suntsu42/iTextDemoInvalidSecondSignature/tree/master

Edit

I've applied the solution provided by MKL to the sample project on github.

As a second note, It is also possible to use PdfSigner but in this case, the bookmarks of the original document must be removed. enter image description here



Solution 1:[1]

As already mentioned in a comment, the example document "InvalidDocumentSignedTwice.pdf" has the signature not applied in an incremental update, so here it is obvious that former signatures will break. But this is not the issue of the OP's example project. Thus, the issue is processed with an eye on the actual outputs of the example project.

Analyzing the Issue

When validating signed PDFs Adobe Acrobat executes two types of checks:

  • It checks the signature itself and whether the revision of the PDF it covers is untouched.
  • (If there are additions to the PDF after the revision covered by the signature:) It checks whether changes applied in incremental updates only consist of allowed changes.

The former check is pretty stable and standard but the second one is very whimsical and prone to incorrect negative validation results. Like in your case...

In case of your example document one can simply determine that the first check must positively validate the first signature: The file with only one (valid!) signature constitutes a byte-wise starting piece of the file with two signatures, so nothing can have been broken here.

Thus, the second type of check, the fickle type, must go wrong in the case at hand.

To find out what change one has to analyze the changes done during signing. A helpful fact is that doing the same using iText 5 does not produce the issue; thus, the change that triggered the check must be in what iText 7 does differently than iText 5 here. And the main difference in this context is that iText 7 has a more thorough tagging support than iText 5 and, therefore, also adds a reference to the new signature field to the document structure tree.

This by itself does not yet trigger the whimsical check, though, it only does so here because one outline element refers to the parent structure tree element of the change as its structure element (SE). Apparently Adobe Acrobat considers the change in the associated structure element as a change of the outline link and, therefore, as a (disallowed) change of the behavior of the document revision signed by the first signature.

So is this an iText error (adding entries to the structure tree) or an Adobe Acrobat error (complaining about the additions)? Well, in a tagged PDF (and your PDF has the corresponding Marked entry set to true) the content including annotations and form fields is expected to be tagged. Thus, addition of structure tree entries for the newly added signature field and its appearance not only should be allowed but actually recommended or even required! So this appears to be an error of Adobe Acrobat.

A Work-Around

Knowing that this appears to be an Adobe Acrobat bug is all well and good, but at the end of the day one might need a way now to sign such documents multiple times without current Adobe Acrobat calling that invalid.

It is possible to make iText believe there is no structure tree and no need to update a structure tree. This can be done by making the initialization of the document tag structure fail. For this we override the PdfDocument method TryInitTagStructure. As the iText PdfSigner creates its document object internally, we do this in an override of the PdfSigner method InitDocument.

I.e. instead of PdfSigner we use the class MySigner defined like this:

public class MySigner : PdfSigner
{
    public MySigner(PdfReader reader, Stream outputStream, StampingProperties properties) : base(reader, outputStream, properties)
    {
    }

    override protected PdfDocument InitDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
    {
        return new MyDocument(reader, writer, properties);
    }
}

public class MyDocument : PdfDocument
{
    public MyDocument(PdfReader reader, PdfWriter writer, StampingProperties properties) : base(reader, writer, properties)
    {

    }

    override protected void TryInitTagStructure(PdfDictionary str)
    {
        structTreeRoot = null;
        structParentIndex = -1;
    }
}

Using MySigner for signing documents iText won't add tagging anymore and so won't make Adobe Acrobat complain about new entries in the structure tree.

The Same Work-Around in Java

As I feel more comfortable working in Java, I analyzed this and tested the work-around in Java.

Here this can be put into a more closed form (maybe it also can for C#, I don't know), instead of initializing the signer like this

PdfSigner pdfSigner = new PdfSigner(pdfReader, os, new StampingProperties().useAppendMode());

we do it like this:

PdfSigner pdfSigner = new PdfSigner(pdfReader, os, new StampingProperties().useAppendMode()) {
    @Override
    protected PdfDocument initDocument(PdfReader reader, PdfWriter writer, StampingProperties properties) {
        return new PdfDocument(reader, writer, properties) {
            @Override
            protected void tryInitTagStructure(PdfDictionary str) {
                structTreeRoot = null;
                structParentIndex = -1;
            }
        };
    }
};

(MultipleSignaturesAndTagging test testSignTestManuelTwiceNoTag)

TL;DR

iText 7 adds a structure element for the new signature field to the document structure tree during signing. If the parent node of this new node is referenced as associated structure element of an outline element, though, Adobe Acrobat incorrectly considers this a disallowed change. As a work-around one can tweak iText signing to not add structure elements.

Solution 2:[2]

I've got a similar problem with signatures after last update of Adobe Reader. I wrote a post on their community, but they still didn't answer me.

Take a look: https://community.adobe.com/t5/acrobat-reader-discussions/invalid-signatures-after-adobe-reader-update-2022-001-20085/td-p/12892048

I am using a iText v.5.5.5 to generate pdf. I sign and certify pdf document in single method. Moreover, foxit reader shows that signatures are valid. I believe that this is an Adobe bug and it will be fixed soon :) Key are an log.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mkl
Solution 2 mastach