0
votes

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();
            }
2
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 duplicateTheGeneral
"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
Refereed your comment. add field before signing and when I fill those field it show siganture is invalid.Pravin Wadkar
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
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

2 Answers

2
votes

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

0
votes

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