Generate PDF from HTML & Integrate Digital Signature into PDF using iTextSharp in C#
August 12, 2017 1 Comment
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(); } } } }
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.