1
votes

I have implemented Digital Signature using iTextSharp Dll to sign PDF files with a single signature creating empty signature fields and update the signature field with signed hash working fine. Now, I want to place the same digital signature in every page of pdf. It's my client requirement.

I’m using the following code:

public class MyExternalSignatureContainer : IExternalSignatureContainer
{
    private readonly byte[] signedBytes;

    public MyExternalSignatureContainer(byte[] signedBytes)
    {
        this.signedBytes = signedBytes;
    }

    public byte[] Sign(Stream data)
    {
        return signedBytes;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
    }
}

Below code used in program

PdfReader reader = new PdfReader(unsignedPdf);
FileStream os = File.OpenWrite(tempPdf);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;

appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 144, 780), 1, null);
for (int i = 1; i < 8; i++)
{
    var signatureField = PdfFormField.CreateSignature(stamper.Writer);
    var signatureRect = new Rectangle(200, 200, 100, 100);
    signatureField.Put(PdfName.T, new PdfString("ClientSignature_"+i.ToString()));
    PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
    signatureField.Put(PdfName.V, PRef);
    signatureField.Put(PdfName.F, new PdfNumber("132"));
    signatureField.SetWidget(signatureRect, null);
    signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);

    PdfDictionary xobject1 = new PdfDictionary();
    PdfDictionary xobject2 = new PdfDictionary();
    xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
    xobject2.Put(PdfName.AP, xobject1);
    signatureField.Put(PdfName.AP, xobject1);
    signatureField.SetPage();
    PdfDictionary xobject3 = new PdfDictionary();
    PdfDictionary xobject4 = new PdfDictionary();
    xobject4.Put(PdfName.FRM, appearance.GetAppearance().IndirectReference);
    xobject3.Put(PdfName.XOBJECT, xobject4);
    signatureField.Put(PdfName.DR, xobject3);

    stamper.AddAnnotation(signatureField, i);
}

IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
stamper.Close();

byte[] SignedHash =  DoEsign(SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
os.close();
reader.close();

reader = new PdfReader(tempPdf))
os = File.OpenWrite(signedPdf)

IExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignedHash);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external1);
os.close();
reader.close();

Please suggest me to complete the task

1
"client requirement" - have you made your client aware of the fact that the current PDF specification ISO 32000-2 dictates "The location of a signature within a document can have a bearing on its legal meaning. For this reason, signature fields shall never refer to more than one annotation." and "A given annotation dictionary shall be referenced from the Annots array of only one page." Thus, that requirements makes the PDF invalid and allows it to be rejected by any viewer, either immediately or during signature validation.mkl
That been said, this answer shows an overview of the options (with comments from before ISO 32000-2); the other answer to the same question gives a hint how to patch iText 5.5.x for your desired result; the patch is described for the Java version but should be easy to port to .Net.mkl
I done the same by using same code. please view it once again.Balasubramaniyan Jayaraman
"I done the same by using same code" - Well, not really. Your approach tries to go yet another road, you don't attempt to reference a single signature field from many pages as is done there (or create multiple widget annotations for a single signature field as done elsewhere), you try to create multiple signature fields sharing the same value! As such an interesting idea. I'm not sure how PDF viewers will react to that if appropriately implemented, as far as I know this is not explicitly forbidden by the specs yet but it's not expected either...mkl
That been said, you have shown your code; have you run it yet? Does it work? If not, what is the issue? (To me your assigning a new indirect reference for the signature value looks spurious. If you want to use the same signature value in all signature fields, all these fields should use the same reference...)mkl

1 Answers

1
votes

To give all signature fields the same single value wrapping the newly created signature container, they must all reference the same indirect object as value. Unfortunately iText creates the indirect object for the signature value only after the application code had the chance to add its additional fields which in turn require a reference to that signature value object. Thus, the application code has to anticipate the object number that indirect object will have.

This anticipation or prediction of the object number is very delicate, it depends on the exact same use case and can also become incorrect as the result of minor changes in the iTextSharp library

To make this easier, the application code should add those signature fields with their signature value references as late as possible, so there are as few other new indirect objects created as possible until iText creates the value indirect object.

As it turns out, the ModifySigningDictionary method of an IExternalSignatureContainer is a good position for that.

As soon as one adds one's code there, another issue pops up: There is no means to set the anticipated object number in a PdfIndirectReference instance externally. One way to get around this is to mimic such a reference using a PdfLiteral. (Well, probably one could also use reflection for this.)

Furthermore it turns out that one best creates the appearance streams to use by all one's additional signature fields before building that PdfLiteral mimicking a PdfIndirectReference as this simplifies the calculation of the object number iText will use for the actual value object.

With this in mind, here a proof-of concept. This proof of concept makes use of an IExternalSignature instance for actually signing. This is not a necessary precondition, one can also use an IExternalSignatureContainer instead with only a few changes, even an ExternalBlankSignatureContainer as in the question to later finalize the signature using MakeSignature.SignDeferred.

So given cipher parameters cp (private key material, e.g. pk.Key for an Org.BouncyCastle.Pkcs.AsymmetricKeyEntry pk) and a certificate chain chain, one would use

PdfReader reader = new PdfReader(SRC);
FileStream os = new FileStream(DEST, FileMode.Create, FileAccess.Write);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;

appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(10, 10, 100, 100), reader.NumberOfPages, null);

IExternalSignature externalSignature = new PrivateKeySignature(cp, "SHA-256");
AllPagesSignatureContainer allPagesContainer = new AllPagesSignatureContainer(appearance, externalSignature, chain);
MakeSignature.SignExternalContainer(appearance, allPagesContainer, 8192);

with this external signature container class

public class AllPagesSignatureContainer : IExternalSignatureContainer
{
    public AllPagesSignatureContainer(PdfSignatureAppearance appearance, IExternalSignature externalSignature, ICollection<X509Certificate> chain)
    {
        this.appearance = appearance;
        this.chain = chain;
        this.externalSignature = externalSignature;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
        signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);

        PdfStamper stamper = appearance.Stamper;
        PdfReader reader = stamper.Reader;
        PdfDictionary xobject1 = new PdfDictionary();
        PdfDictionary xobject2 = new PdfDictionary();
        xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
        xobject2.Put(PdfName.AP, xobject1);

        PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
        PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");

        for (int i = 1; i < reader.NumberOfPages; i++)
        {
            var signatureField = PdfFormField.CreateSignature(stamper.Writer);

            signatureField.Put(PdfName.T, new PdfString("ClientSignature_" + i.ToString()));
            signatureField.Put(PdfName.V, PRefLiteral);
            signatureField.Put(PdfName.F, new PdfNumber("132"));
            signatureField.SetWidget(appearance.Rect, null);
            signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);

            signatureField.Put(PdfName.AP, xobject1);
            signatureField.SetPage();
            Console.WriteLine(signatureField);

            stamper.AddAnnotation(signatureField, i);
        }
    }

    public byte[] Sign(Stream data)
    {
        String hashAlgorithm = externalSignature.GetHashAlgorithm();
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
        IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
        byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
        byte[] extSignature = externalSignature.Sign(sh);
        sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
        return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
    }

    PdfSignatureAppearance appearance;
    ICollection<X509Certificate> chain;
    IExternalSignature externalSignature;
}

The predicted indirect object number of the signature value in the line

PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");

strictly depends upon the use case being "exactly one signature field per page". For different use cases the estimate the prediction would differ.

I stress this here once again because e.g. the OP of this question did not take this into account when trying "to place multiple signatures on single page".

Another strict requirement for the object number prediction above is that the PdfStamper is created as above, i.e. not in append mode. If the signature is applied as an incremental update, i.e. in append mode, the lines above have to be replaced by

stamper.Writer.AddToBody(new PdfNull(), stamper.Writer.PdfIndirectReference, true);

PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");

This made a difference in the context of this question; the first line, adding an indirect null object to the PDF, is necessary to make sure that in case of PDFs with object streams the object stream object number has already been determined and does not slip between the next objects, resulting in an off-by-one error for our prediction.


Beware: While this procedure creates something which does not violate the letter of the PDF specifications (which only forbid the cases where the same field object is referenced from multiple pages, be it via the same or via distinct widgets), it clearly does violate its intent, its spirit. Thus, this procedure might also become forbidden as part of a Corrigenda document for the specification.