0

Before calculating the hash of the document for signing I am adding the TextField in my document using the below code. as I am following this link Changing signature appearance after signing pdf file with iTextSharp Here is a code that adds signature on all pages and adds a text field on the first page. the purpose of the text field is to extract the "IssuedTo" from the certificate and display it on the signature appearance.

Before esign open pdf in update mode:

 XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signatures");

                    string signature = nodeList[0].FirstChild.InnerText;

                    string src = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_temp.pdf");
                    string dest = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_multiple_signed.pdf");
                    ///add text
                    AddText(src, dest);
                    ///add text
                    using (PdfReader reader = new PdfReader(src))
                    {
                        using (FileStream os = new FileStream(dest, FileMode.Create))
                        {
                            byte[] encodedSignature = Convert.FromBase64String(signature);

                            IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
                            MakeSignature.SignDeferred(reader, "sign1", os, external);
                        }
                    }

Code that add text to temp pdf

 public void AddText(String src, String dest) {
                PdfReader reader = new PdfReader(src);
                PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create), '\0', true);
                ColumnText.ShowTextAligned(stamper.GetOverContent(1), Element.ALIGN_LEFT, new Phrase("client name"), 200, 380, 0);
                stamper.Close();
            }
Pravin Wadkar
  • 63
  • 1
  • 7
  • if your question is `Is it possible to change the appearance of the signature within the document after signing it?` then you have posted your own duplicate – TheGeneral May 03 '19 at 06:09
  • *"I am adding the TextField"* - which text field? The linked question does not refer to any text field, and there isn't a canonical text field in the signing process either. (Or do you refer to my comment to Bruno's answer there?) – mkl May 03 '19 at 06:13
  • Refereed your comment. add field before signing and when I fill those field it show siganture is invalid. – Pravin Wadkar May 03 '19 at 06:22
  • Your *code that stamps the signature on the document* uses `os` both for a `PdfStamper` and for `MakeSignature.SignDeferred`. This produces a hodgepodge of two pdfs in the stream. Even if a pdf reader can repair it, any signature in it will be broken due to the repair changes. – mkl May 03 '19 at 08:48
  • Signature on all pages of PDF is needless... what gets signed is complete pdf hash and single signature proves non repudiation of complete pdf document. – Bharat Vasant May 04 '19 at 01:00
  • *"Signature on all pages of PDF is needless..."* - apparently there are some contexts for which clients require signature visualizations on each page. Other than such requirements such signatures are indeed needless. – mkl May 04 '19 at 09:29
  • Yes,its signature visualization on all pages. how do I add/update signature visualizations after signed? – Pravin Wadkar May 06 '19 at 06:34
  • [This answer](https://stackoverflow.com/a/37070234/1729265) might help you along (it is for iText and Java but should be easy to adapt for iTextSharp and C#). – mkl May 06 '19 at 09:26
  • As you removed your specific signing code, I assume you are now looking for a generic solution changing your signature appearances... – mkl May 07 '19 at 09:00

2 Answers2

3

First of all, as has been discussed in comments to the question and to Bharat's answer:

The need to update signature appearances after applying the signature indicates a bad architecture of the signing solution.

In the case at hand this bad architecture appears to be a result of the requirements ("appearances must include certificate information" in combination with "certificate is not available before signing"). Nonetheless this is a bad architecture and should be improved after reviewing and revising the requirements.

But it indeed is possible under benign circumstances to update signature appearances: If the existing signatures allow for "form fill-ins and annotation changes" and do not completely lock the respective signature fields, the appearances of signatures can be updated in an incremental update without invalidating the signatures (validators may warn about a change, though).

Updating generic PDF signatures

The PDF specification does not specifically define a structure for the appearance of a signature field, a generic solution simply has to replace the appearance stream of each signature field widget annotation with a new one. This can be done like this using iText 5.5.x for .Net:

using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
    AcroFields acroFields = pdfStamper.AcroFields;
    foreach (String signatureName in acroFields.GetSignatureNames())
    {
        PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
        X509Certificate signerCert = pkcs7.SigningCertificate;
        String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");

        PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, 100, 100);
        ColumnText columnText = new ColumnText(appearance);
        Chunk chunk = new Chunk();
        chunk.SetSkew(0, 12);
        chunk.Append("Signed by:");
        columnText.AddElement(new Paragraph(chunk));
        chunk = new Chunk();
        chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
        chunk.Append(signerName);
        columnText.AddElement(new Paragraph(chunk));
        columnText.SetSimpleColumn(0, 0, 100, 100);
        columnText.Go();

        PdfDictionary appDict = new PdfDictionary();
        appDict.Put(PdfName.N, appearance.IndirectReference);

        AcroFields.Item field = acroFields.GetFieldItem(signatureName);
        for (int i = 0; i < field.Size; i++)
        {
            PdfDictionary widget = field.GetWidget(i);
            PdfArray rect = widget.GetAsArray(PdfName.RECT);
            float x = Math.Min(rect.GetAsNumber(0).FloatValue, rect.GetAsNumber(0).FloatValue);
            float y = Math.Min(rect.GetAsNumber(1).FloatValue, rect.GetAsNumber(3).FloatValue);
            widget.Put(PdfName.RECT, new PdfArray(new float[] { x, y, x + 100, y + 100 }));
        }
        field.WriteToAll(PdfName.AP, appDict, AcroFields.Item.WRITE_WIDGET);
        field.MarkUsed(acroFields, AcroFields.Item.WRITE_WIDGET);
    }
}

As you can see the code extracts the subject's common name from the signer certificate and writes it (prefixed by a "Signed by:" line) to the new appearance. If you need other data in your your replacement appearance, simply change the data added to the columnText and/or the appearance accordingly.

Furthermore, the code replace all appearances with new ones which are 100×100 in size. You of course can adapt this to your requirements, too.

This essentially is a port of code from this answer to C#.

Updating PDF signatures using the Adobe specific layers

Adobe Acrobat Reader uses a specific scheme to build its signature appearances and even adds a certain functionality to signatures built according to an older version of this scheme. As mentioned above, the PDF specification does not prescribe any such scheme; actually it even forbids such a functionality, cf. this answer.

Nonetheless many stack overflow questions in particular from India seem to indicate that signatures following that obsolete scheme are often required there by clients.

If one follows this scheme, the appearance itself is constructed as a hierarchy of form XObjects, in particular a set of so called "layers" n0 through n4 from which n2 is the layer onto which a signer is expected to apply its identity.

The generic solution above can be adapted as follows to comply with this scheme:

using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
    AcroFields acroFields = pdfStamper.AcroFields;
    foreach (String signatureName in acroFields.GetSignatureNames())
    {
        PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
        X509Certificate signerCert = pkcs7.SigningCertificate;
        String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");

        AcroFields.Item field = acroFields.GetFieldItem(signatureName);
        for (int i = 0; i < field.Size; i++)
        {
            PdfDictionary widget = field.GetWidget(i);
            Rectangle rect = PdfReader.GetNormalizedRectangle(widget.GetAsArray(PdfName.RECT));

            PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, rect.Width, rect.Height);
            ColumnText columnText = new ColumnText(appearance);
            Chunk chunk = new Chunk();
            chunk.SetSkew(0, 12);
            chunk.Append("Signed by:");
            columnText.AddElement(new Paragraph(chunk));
            chunk = new Chunk();
            chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
            chunk.Append(signerName);
            columnText.AddElement(new Paragraph(chunk));
            columnText.SetSimpleColumn(0, 0, rect.Width, rect.Height - 15);
            columnText.Go();

            PdfDictionary xObjects = GetAsDictAndMarkUsed((PdfStamperImp)pdfStamper.Writer, widget, PdfName.AP, PdfName.N, PdfName.RESOURCES, PdfName.XOBJECT, PdfName.FRM, PdfName.RESOURCES, PdfName.XOBJECT);
            xObjects.Put(PdfName.N2, appearance.IndirectReference);
        }
    }
}

making use of the following helper method:

PdfDictionary GetAsDictAndMarkUsed(PdfStamperImp writer, PdfDictionary dictionary, params PdfName[] names)
{
    PRIndirectReference reference = null;
    foreach (PdfName name in names)
    {
        if (dictionary != null)
        {
            dictionary = dictionary.GetDirectObject(name) as PdfDictionary;
            if (dictionary != null)
            {
                if (dictionary.IndRef != null)
                    reference = dictionary.IndRef;
            }
        }
    }
    if (reference != null)
        writer.MarkUsed(reference);

    return dictionary;
}

(Beware: This code assumes the signatures to follow the Adobe scheme; if you are not sure that your input does, add some sanity checks and default to the generic solution above.)

Community
  • 1
  • 1
mkl
  • 90,588
  • 15
  • 125
  • 265
  • Hi @mkl I have the exact requirement but need to do this with iText7. Does iText7 support this? – Muddassir Awan Sep 20 '22 at 20:59
  • @MuddassirAwan *"Does iText7 support this?"* - yes, see [this answer](https://stackoverflow.com/a/37070234/1729265) referenced above. – mkl Sep 21 '22 at 04:43
0

Since appearance is part of document while calculating hash to be signed, changing appearance will change hash and invalidate the signature already done.

Bharat Vasant
  • 850
  • 3
  • 12
  • 46
  • *"changing appearance will change hash"* - it won't if the appearance is changed in an incremental update. – mkl May 04 '19 at 06:16
  • @mkl can you please elaborate what is incremental update? – Pravin Wadkar May 06 '19 at 05:35
  • @Bharat its requirement to print the "isssueTo"(i.e client name) on signature appearance section along with signed date. and that "IssuedTo" is not available before calling the signer service. – Pravin Wadkar May 06 '19 at 06:16
  • signer service return the certificate. that contain "issueTo". How do i print that on my pdf in appearance section – Pravin Wadkar May 06 '19 at 06:18
  • 1
    @Pravin *"what is incremental update?"* - If you change a PDF not by saving a completely new dump of PDF objects but by appending only the changes to the original PDF. In iText lingo also known as "append mode". – mkl May 06 '19 at 09:10
  • @Pravin *"its requirement to print the "isssueTo"(i.e client name) on signature appearance section along with signed date. and that "IssuedTo" is not available before calling the signer service."* - You probably should tell the client that implementing the requirement using that signing service will result in a hack of some kind, only kind of working under specific circumstances, a fragile construct. And then ask them whether they'll keep that requirement and accept a fragile solution (later fixes to make it work again costing extra money) or whether they'll drop it. – mkl May 06 '19 at 09:20
  • @mkl code updated. open file in append mode. add text. even all signatures are valid but text is not visible to page. – Pravin Wadkar May 06 '19 at 10:18
  • You can't add text to a pdf by appending it to the end, it's not that simple. – Lasse V. Karlsen May 06 '19 at 10:31
  • @LasseVågsætherKarlsen is there any other way to change signature visualization ? – Pravin Wadkar May 06 '19 at 10:37
  • Either you need separate API for getting Certificate and Signing or you need to call API two times and ignore signing in the first call and only use required information and redo signing in next call. – Bharat Vasant May 06 '19 at 11:31
  • https://stackoverflow.com/a/21411888/10655739 here is comment by @Axel Örn Sigurðsson. he achieved this same scenario. but I am not getting idea how to do that code – Pravin Wadkar May 06 '19 at 11:47
  • @BharatVasant calling same service twice wan't possible. It will increase my signature costing. – Pravin Wadkar May 06 '19 at 11:48
  • @Pravin Nonetheless Bharat is right: If your client insists on that information in the signature visualization, you have to determine the used certificate before signing for a non hack'ish solution. So if calling the service twice can be assumed to have the same certificate used in both cases, calling twice should be done. – mkl May 06 '19 at 14:52
  • Yes, we also offer **GetCertificate** and **Sign** as two separate API that may be called by passing same signing profile name under our multi-tenant HSM offering - [Kluis](https://signer.digital/kluis) – Bharat Vasant May 06 '19 at 15:46
  • I am doing aadhaar(unique identification number issued by the Indian government to every individual resident of India) based signature. – Pravin Wadkar May 07 '19 at 04:24