I am writing a demo proof of concept to add electronic signatures to existing PDFs. I am running into one weird problem that I don't get. It seems that adding the signature to some documents works fine while adding one to others does not work and a corrupt file is generated that Adobe Reader can not open.
Here is my code:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using iText;using iText.Kernel.Pdf;using System.IO;using iText.Layout;using iText.Layout.Element;using iText.Kernel.Geom;using Org.BouncyCastle.Crypto.Tls;using iText.Signatures;using System.Collections.ObjectModel;using Org.BouncyCastle.Pkcs;using System.Security.Cryptography.X509Certificates;using Org.BouncyCastle.Crypto;using System.Security.Cryptography;using Org.BouncyCastle.Crypto.Parameters;using Org.BouncyCastle.Math;using iText.IO.Image;namespace LTVSkilrikjaDemo{ class Program { static void Main(string[] args) { string welcomeText = "Welcome to LTVSkilríkjaDemotolid!"; string pressEnterToTry = "Commands: 's' - sign, 'stop' - stops the programme"; Console.WriteLine(welcomeText); Console.WriteLine(pressEnterToTry); // Base directory prepared string basedir = AppDomain.CurrentDomain.BaseDirectory; int index = basedir.IndexOf(@"bin\"); basedir = basedir.Remove(index); string readString = Console.ReadLine().ToLower(); while(!readString.Equals("stop")) { if(readString.Equals("c")) { string inFile = "Infile2.pdf"; string outFile = "Outfile2.pdf"; // Open PDF document and decide where to write the new document PdfWorker worker = new PdfWorker(); worker.ReadPdf(basedir +"App_Data\\InFiles\\"+ inFile, basedir +"App_Data\\OutFiles\\"+ outFile); // Start working on certificate X509Store store = new X509Store("My"); store.Open(OpenFlags.ReadOnly); Collection<Org.BouncyCastle.X509.X509Certificate> xcertificates = new Collection<Org.BouncyCastle.X509.X509Certificate>(); foreach (X509Certificate2 mCert in store.Certificates) { if (mCert.Subject.IndexOf("CN=Róbert") > -1) { xcertificates.Add(Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(mCert)); } } Org.BouncyCastle.X509.X509Certificate[] certificatesProcessed = new Org.BouncyCastle.X509.X509Certificate[xcertificates.Count]; for(int i = 0; i < xcertificates.Count; i++) { certificatesProcessed[i] = xcertificates[i]; } var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(store.Certificates[5].PrivateKey).Private; try { worker.Sign(certificatesProcessed, pk, DigestAlgorithms.SHA1, PdfSigner.CryptoStandard.CADES, "No apparent raisin!", "Lost in Iceland", null, null, null, 0, true, basedir); } catch(Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error! "+ ex.Message +"\n\r"+ ex.StackTrace); if(ex.InnerException != null) { Console.WriteLine("Inner exception: "+ ex.InnerException.Message); } Console.ForegroundColor = ConsoleColor.Gray; } } else if(!readString.Equals("stop")) { Console.WriteLine("Command not understood. Understand only 's' and 'stop'."); } readString = Console.ReadLine(); } Console.WriteLine("Goodbye!"); System.Threading.Thread.Sleep(500); } } public class PdfWorker { private PdfDocument _document; private string _source; private string _dest; public void ReadPdf(string source, string dest) { _source = source; _dest = dest; } public void Sign(Org.BouncyCastle.X509.X509Certificate[] chain, Org.BouncyCastle.Crypto.ICipherParameters pk, string digestAlgorithm, PdfSigner.CryptoStandard subfilter, string reason, string location, Collection<ICrlClient> crlList, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize, bool initial, string baseDir) { File.Copy(_source, _dest, true); FileStream f = new FileStream(_dest, FileMode.Append); try { PdfSigner signer = new PdfSigner(new PdfReader(_source), f, true); _document = signer.GetDocument(); _document.AddNewPage(); // Work the last page Rectangle pageSize = _document.GetLastPage().GetPageSizeWithRotation(); //PdfWriter w = _document.GetWriter(); //long currentPos = w.GetCurrentPos(); float llx = pageSize.GetWidth() - 350 - 20; //pageSize.GetWidth() / 2 - 350 / 2; float lly = pageSize.GetHeight() - 50 - 20; // pageSize.GetHeight() / 2 - 150 / 2; float urx = 350; //llx + 350; float ury = 50; //lly + 150; PdfSignatureAppearance appearance = signer.GetSignatureAppearance(); appearance.SetPageRect(new Rectangle(llx, lly, urx, ury)); appearance.SetReason(reason); appearance.SetLocation(location); byte[] imagebytes = File.ReadAllBytes(baseDir +"App_Data\\UndirskriftDemo.png"); // It is not possible to use the path as it contains Icelandic characters // which itext chokes on. We use byte array instead ImageData imgData = ImageDataFactory.Create(imagebytes); Image img = new Image(imgData); img = img.ScaleToFit(350.0f, 50.0f); appearance.SetImage(imgData); int pageCount = _document.GetNumberOfPages(); // Creating the appearance if(initial == true) { signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS); } appearance.SetPageNumber(pageCount); Rectangle rect = new Rectangle(10, 50, 350, 50); appearance.SetPageRect(rect).SetPageNumber(pageCount); appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION); signer.SetFieldName(signer.GetNewSigFieldName()); // Creating the signature IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm); signer.SignDetached(pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter); Console.WriteLine("Signing successful!"); } catch(Exception ex) { throw ex; } finally { _document.Close(); f.Close(); } } }}
Please see the documents here then. Infile1.pdf I can not sign but Infile2.pdf is signed fine. Outfile1.pdf is the corrupt file.https://app.box.com/s/52jqe8qirl80km6hunxucs00dntx70o5
What causes this? Is there something about the input PDF file or the above programme?