2

I am trying to add multiple signatures with iTextSharp 5.5.13.1.
Only the last signature is valid.
And all previous signatures are invalid with the message: "Document has been altered or corrupted since it was signed" - 1 Page(s) Modified

I don't necessarily need certified signatures.

I use signature append mode but still can't figure out what modifies the document. In notepad the first part of document till the second signature seems unchanged.

The code I used is:

private string SignFile(string fileToSign, string certname, float xPercent, float yPercent, int page)
{
    string signedFile = fileToSign.Replace(".pdf", ".signed.pdf");

    using (PdfReader pdfReader = new PdfReader(fileToSign))
    {
        int pages = pdfReader.NumberOfPages;
        var currentSignaturesCount = pdfReader.AcroFields.GetSignatureNames().Count();

        using (FileStream signedPdf = new FileStream(signedFile, FileMode.Create, FileAccess.ReadWrite))
        {
            string tempDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ".tempfiles");
            Directory.CreateDirectory(tempDir);
            string tempFileName = Path.Combine(tempDir, Guid.NewGuid().ToString("N") + ".pdf");
            if (!File.Exists(tempFileName))
                File.Create(tempFileName).Close();

            using (PdfStamper pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0', tempFileName, true))  // Append mode
            {
                // Add signature image
                if (page <= pages && page > 0)
                {
                    var pdfContentByte = pdfStamper.GetOverContent(page);

                    var pageSize = pdfReader.GetPageSize(i);
                    float pageWidth = pageSize.Width;
                    float pageHeight = pageSize.Height;

                    // GenerateStamp() = simplified function that will get a custom bitmap (code not included here)
                    System.Drawing.Bitmap img = GenerateStamp();

                    var image = iTextSharp.text.Image.GetInstance(img, true);

                    image.SetAbsolutePosition(xPercent * pageWidth, pageHeight - yPercent * pageHeight - image.ScaledHeight);

                    pdfContentByte.AddImage(image);
                }
                //Also tried adding the image directly to signatureAppearance
                //signatureAppearance.SignatureGraphic = image;
                //signatureAppearance.SetVisibleSignature(rectangle, page, signatureFieldName);
                // and getting the error "Document has been altered or corrupted since it was signed"


                PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

                signatureAppearance.Reason = "Test";
                signatureAppearance.SignDate = DateTime.Now;
                signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC;
                signatureAppearance.Acro6Layers = false;

                //Also tried like this:
                //signatureAppearance.CertificationLevel = currentSignaturesCount == 0 ? PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS : PdfSignatureAppearance.NOT_CERTIFIED;
                // with message: "There have been changes made to this document that invalidate the signature"

                // sign document
                try
                {
                    X509Certificate2 cert = GetCertificateByName(certname);
                    Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
                    Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(cert.RawData) };
                    IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-256");

                    MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
                }
                catch (Exception ex)
                {
                    throw;
                }

            }
        }
    }

    return signedFile;
}

Any help is appreciated. Thanks

gabrielgeo
  • 507
  • 2
  • 6
  • 17
  • Your code as is adds an image to a page. That is forbidden to do to a signed file. According to your comments, though, you also tried to add the image to the signature appearance. That is not forbidden as such. Please focus on that approach and share problem files based on that approach. – mkl May 15 '20 at 19:17
  • I have added the pdf files with image in signature appearance. – gabrielgeo May 16 '20 at 03:29
  • You still change the page, at lest there are additional page content streams. Probably you still call `var pdfContentByte = pdfStamper.GetOverContent(page);`. That call already changes the page by adding a content stream for the over content. – mkl May 16 '20 at 08:07
  • I see. I need to take the page width and height to position the image. I didn't knew that a simple Get creates additional content stream. Let me check it. Thanks – gabrielgeo May 16 '20 at 09:23
  • *"I need to take the page width and height to position the image"* - you can retrieve those information from the `PdfReader`. *"I didn't knew that a simple Get creates additional content stream."* - strictly speaking it should be `CreateOrGetOverContent`. But at least it is idempotent, once a stamper has an overcontent for a page, it uses it again and again. – mkl May 16 '20 at 09:27
  • I checked it and it works! Actually I didn't need to call GetOverContent as I took the page size with pdfReader.GetPageSize(page). The code escaped my check. Thank you again! Please add it as answer. – gabrielgeo May 16 '20 at 09:32

1 Answers1

1

Your code as is adds an image to the static content of a page. That is forbidden to do to a signed file. For details on allowed and disallowed changes to a signed PDF read this answer.

According to your code comments, though, you also tried to alternatively add the image to the signature appearance. That is not forbidden as such. But analyzing the provided example PDFs it becomes apparent that in this attempt additional content streams have been added to the page. Even though they essentially are empty, this is considered a change of page content which is disallowed.

As it turned out, you didn't add the image to the page content in this attempt but you still retrieved the OverContent of the page:

var pdfContentByte = pdfStamper.GetOverContent(page);

This operation already adds extra content streams to the page for the OverContent to come. Strictly speaking, therefore, the method should be named CreateOrGetOverContent instead...

After removing the GetOverContent call signing does not damage the previous signatures anymore.

mkl
  • 90,588
  • 15
  • 125
  • 265