Generate PDF from HTML & Integrate Digital Signature into PDF using iTextSharp in C#

using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

public partial class _Default : System.Web.UI.Page
{
    public enum HashType
    {
        SHA1withDSA, //-- DSA
        SHA1withECDSA, //
        SHA224withECDSA, // ECDSA with SHA1 and SHA2 support
        SHA256withECDSA, //
        SHA384withECDSA, //
        SHA512withECDSA, //
        MD2withRSA, // --
        MD5withRSA, // --
        SHA1withRSA, // --
        SHA224withRSA, // -- RSA with MD2, MD5, SHA1, SHA2 and RIPEMD
        SHA256withRSA, // --
        SHA384withRSA, // --
        SHA512withRSA, // --
        RIPEMD160withRSA, // -- RIPEMD hash
        RIPEMD128withRSA, // --
        RIPEMD256withRSA, // --
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        CreatePDF();
    }

    private void CreatePDF()
    {
        //Create a byte array that will eventually hold our final PDF
        Byte[] bytes;

        //Boilerplate iTextSharp setup here
        //Create a stream that we can write to, in this case a MemoryStream
        using (var ms = new MemoryStream())
        {
            //Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
            using (var doc = new Document(PageSize.A4, 0f, 0f, 0f, 0f))
            {
                //Create a writer that's bound to our PDF abstraction and our stream
                using (var writer = PdfWriter.GetInstance(doc, ms))
                {
                    //Open the document for writing
                    doc.Open();
                    string contents = File.ReadAllText(@"F:\PROJECTS\WebSite6\HTML\a.html");

                    /**************************************************
                     * Use the XMLWorker to parse the HTML.           *
                     * Only inline CSS and absolutely linked          *
                     * CSS is supported                               *
                     * ************************************************/
                    //XMLWorker also reads from a TextReader and not directly from a string
                    using (var srHtml = new StringReader(contents))
                    {
                        //Parse the HTML
                        iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
                    }                   
                    doc.Close();
                }
            }
            //After all of the PDF "stuff" above is done and closed but **before** we
            //close the MemoryStream, grab all of the active bytes from the stream
            bytes = ms.ToArray();
        }

        //Now we just need to do something with those bytes.
        //Here I'm writing them to disk but if you were in ASP.Net you might Response.BinaryWrite() them.
        //You could also write the bytes to a database in a varbinary() column (but please don't) or you
        //could pass them to another function for further PDF processing.
        var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
        System.IO.File.WriteAllBytes(testFile, bytes);
        //===================
        GeneratorKey();
        //===================

        Stream fs = File.OpenRead(@"F:/PROJECTS/WebSite6/HTML/TestCert.pfx");

        string sourceDocument = testFile;
        string destinationPath = testFile.Replace(".pdf", "_signed.pdf");
        Stream privateKeyStream = fs;
        string keyPassword = "123";
        string reason = "I am an Author";
        string location = "Mumbai";

        _Default.signPdfFile(sourceDocument, destinationPath, privateKeyStream, keyPassword, reason, location);

        fs.Close();
    }

    private void GeneratorKey()
    {
        // Keypair Generator
        RsaKeyPairGenerator kpGenerator = new RsaKeyPairGenerator();
        kpGenerator.Init(new KeyGenerationParameters(new SecureRandom(), 2048));

        // Create a keypair
        AsymmetricCipherKeyPair kp = kpGenerator.GenerateKeyPair();

        // Certificate Generator
        X509V3CertificateGenerator cGenerator = new X509V3CertificateGenerator();
        cGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
        cGenerator.SetSubjectDN(new X509Name("CN=" + "domain.com"));
        cGenerator.SetIssuerDN(new X509Name("CN=" + "Amit"));
        cGenerator.SetNotBefore(DateTime.Now);
        cGenerator.SetNotAfter(DateTime.Now.Add(new TimeSpan(365, 0, 0, 0))); // Expire in 1 year
        cGenerator.SetSignatureAlgorithm(HashType.SHA256withRSA.ToString()); // See the Appendix Below for info on the hash types supported by Bouncy Castle C#
        cGenerator.SetPublicKey(kp.Public); // Only the public key should be used here!
        X509Certificate cert = cGenerator.Generate(kp.Private); // Create a self-signed cert

        byte[] encoded = cert.GetEncoded();
        using (FileStream outStream = new FileStream("F:/PROJECTS/WebSite6/HTML/TestCert.der", FileMode.Create, FileAccess.ReadWrite))
        {
            outStream.Write(encoded, 0, encoded.Length);
        }

        // Create the PKCS12 store
        Pkcs12Store store = new Pkcs12StoreBuilder().Build();

        // Add a Certificate entry
        X509CertificateEntry certEntry = new X509CertificateEntry(cert);
        store.SetCertificateEntry(cert.SubjectDN.ToString(), certEntry); // use DN as the Alias.

        // Add a key entry
        AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(kp.Private);
        store.SetKeyEntry(cert.SubjectDN.ToString() + "_key", keyEntry, new X509CertificateEntry[] { certEntry }); // Note that we only have 1 cert in the 'chain'

        // Save to the file system
        using (var filestream = new FileStream(@"F:/PROJECTS/WebSite6/HTML/TestCert.pfx", FileMode.Create, FileAccess.ReadWrite))
        {
            store.Save(filestream, "123".ToCharArray(), new SecureRandom());
        }
    }

    public static void signPdfFile(string sourceDocument, string destinationPath, Stream privateKeyStream, string keyPassword, string reason, string location)
    {
        Pkcs12Store pk12 = new Pkcs12Store(privateKeyStream, keyPassword.ToCharArray());
        privateKeyStream.Dispose();

        //then Iterate throught certificate entries to find the private key entry
        string alias = null;
        foreach (string tAlias in pk12.Aliases)
        {
            if (pk12.IsKeyEntry(tAlias))
            {
                alias = tAlias; break;
            }
        }

        var pk = pk12.GetKey(alias).Key;
        // reader and stamper
        PdfReader reader = new PdfReader(sourceDocument);
        using (FileStream fout = new FileStream(destinationPath, FileMode.Create, FileAccess.ReadWrite))
        {
            using (PdfStamper stamper = PdfStamper.CreateSignature(reader, fout, '\0'))
            {
                // appearance
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                //appearance.Image = new iTextSharp.text.pdf.PdfImage();
                appearance.Reason = reason; appearance.Location = location;
                appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(20, 10, 170, 60), 1, "Icsi-Vendor");
                // digital signature
                IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
                MakeSignature.SignDetached(appearance, es, new X509Certificate[] { pk12.GetCertificate(alias).Certificate }, null, null, null, 0, CryptoStandard.CMS);
                stamper.Close();
            }
        }
    }
}

One Response to Generate PDF from HTML & Integrate Digital Signature into PDF using iTextSharp in C#

  1. Kunal Saini says:

    Thank you for sharing this informative and well-crafted blog post. The content was well-researched and presented in a way that was easy to understand. I liked how you broke down complex concepts into digestible chunks. To explore further, click here.

Leave a comment