PDF Digital Signatures with iText7, Bouncy Castle and .Net Core

ITextSharp is a wonderful library written to interact with PDF or create new PDF files, the latest version available is called iText7. Since ISO 320001 , PDF is a standard portable document interface and its extensive support for digital signatures makes PDF the default go to document types to preserve document integrity in business scenarios. Corporate businesses and now even small enterprises look for solutions related to digital signatures from the DocuSign, EchoSign, SigningHub, RightSignatures, ZohoSign and other big companies providing solutions for digital signatures with document workflows.

Lets dive into what these digital signatures are and how they are really performed programmatically using iText7 with bouncy castle and .Net Core.

Brief overview

Digital certificates bind a certain state of the document to a digital certificate, approving that this state of the document was acknowledged by the owner of the digital certificate at a certain time. Digital certificates are the identities issued to a legal entity by the certified CAs (Certificate Authorities) proving that wherever the private key of this certificate is used it can only be used by the owner of the digital certificate to whom it was issued. These processes and standards are governed by the laws in most of the world and the emerging markets are adapting them swiftly to automate their current processes.

Another important concept for digital signing is to sign the hash of the document instead of complete document. Usually the hash or digest of the document is always a definite length of bytes that is calculated from the whole document. Its always unique and can be calculated as a different value even by changing a single bit in the document. This helps in proving the integrity of the document later in the verification of the digital signatures. We will look in to the verification of digital signatures later in another blog.

Setting the stage

First of all, we need a certificate. Either issued by a legal authority like an accredited CA or a self signed certificate. If your company works with an internal CA and creates certificate internally for the employees that will work as well. For a get going, lets create a self signed certificate with the easiest method for now using https://certificatetools.com/.

Start by adding John Doe as a sample Common Name.

Next, expand the Key Usage and select the Digital Signatures from the list.

I will suggest as a newbie, lets not touch any other option for now, considering not that it will break anything but it will can add confusions.

Now scroll to the end towards CSR Options and select self signed from the second drop down.

Press submit button and new options will appear after a quick processing

Download PKCS#12 Certificate and Key. The resultant file downloaded to the system is a PFX file which contains certificate as well as private and public keys for this newly created certificate.

Assuming that we already had a PDF file and now that we have a certificate as well, we can continue to sign the PDF with this newly created certificate.

Start a new project in Visual studio 2017 or above. Select a Visual C# template for .Net Core application. As its a basic level signatures, we will be starting with a simple Console App.

For each of access, create a folder on your D drive (or any drive that is accessible via code). I have created a new folder with the name resources in D drive and placed my self signed certificate and the sample PDF file in the folder.

Adding iText7

The first thing before starting the code is to add the reference to the community version of the iText.7. iText7 comes with two licenses option, one is GPL license for open source projects and an other is commercial license. Nuget command to install itext7 package is as follows

PM> install-package itext7

Once itext is added the we can see that in the project explorer in visual studio as follows

Note that it already imports the portable version of the bouncy castle. While writing current stable version of itext is 7.1.9 which comes with a bouncy castle library of version 1.8.1.3. If we upgrade bouncy castle to 1.8.5 it will break the application as few implementations on which iText7 is dependent are removed in bouncy castle 1.8.5.

private key & certificate chain

Now lets load read the PFX/P12 file that we have saved in the resources in C# using bouncy castle.

//following bouncy castle libraries are to be referenced
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;

By having bouncy castle references in our code we can call the following methods to read the Keystore (PFX/P12 file) and extract the private key and chain of the certificate. Every P12 file is protected with a password hence password is needed to open the store access the keys. This password is usually known the end users only. Which means in real application, we will need to ask for this password from the end user.

string KEYSTORE = "D:\\resources\\cert.pfx";
char[] PASSWORD = "password".ToCharArray();

Pkcs12Store pk12 = new Pkcs12Store(new FileStream(KEYSTORE, 
FileMode.Open, FileAccess.Read), PASSWORD);
string alias = null;
foreach(object a in pk12.Aliases)
{
    alias = ((string)a);
    if(pk12.IsKeyEntry(alias))
    {
        break;
    }
}
ICipherParameters pk = pk12.GetKey(alias).Key;

pk variable now have a private key extracted from the certificate that was found in the store.

Certificates are usually divided (but not limited to) in to three types i.e., root certificates, intermediate certificates and end entity certificates. Human identities and certificates are usually the end entity certificates which are issued by either an intermediate or a root CA. Therefore we need to maintain the chain of certificates while adding this identity into the PDF so that later while verifying the verification server can verify the certificates of end entity along with all the intermediate and root CA certificates.

X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
X509Certificate[] chain = new X509Certificate[ce.Length];
for(int k = 0 ; k < ce.Length ; ++k)
{
    chain[k] = ce[k].Certificate;
}

reading/opening pdf file

Now that we have the chain and the private key to sign the PDF file with, lets read the PDF file

string DEST = "D:\\resources\\SignedPDF.pdf";
string SRC = "D:\\resources\\SampleContract.pdf";

PdfReader reader = new PdfReader(SRC);
PdfSigner signer = new PdfSigner(reader, 
new FileStream(DEST, FileMode.Create), 
new StampingProperties());

To read large files we can use another constructor of PdfSigner which also asks for a temp file path. If temp path file is set, iText saves the intermediate state of file on the temp path. iText will try to delete the file from the temp path at the end if output stream is set. If output stream is null, temp file persist read the last changes to the content after the signature.

Creating signature field

To create a signature field we need to create a signature appearance object from PdfSigner object.

PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
appearance.SetReason("My reason to sign...")
    .SetLocation("Lahore")
    .SetPageRect(new Rectangle(36, 648, 200, 100))
    .SetPageNumber(1);
signer.SetFieldName("MyFieldName");    

Above code creates a new field with the name “MyFieldName” and set the reason and location for signature in the PDF before signing the field. It also specifies the coordinates where the signature box will be created in the document and the page of PDF on which the new field will be created.

If you want to use an existing field to sign the document use the following code snippet. It removes the page rectangle and page number from the above code. Then iText looks for an existing field with the name “MyFieldName”.

PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
appearance.SetReason("My reason to sign...")
    .SetLocation("Lahore");
signer.SetFieldName("MyFieldName"); 

Create an External Signature Object

We are almost all set, iText comes with only one implementation of IExternalSignature which is the basic implementation of PDF signatures with a private key (PrivateKeySignature). We will pass the private key that was extracted from the P12/PFX file previously.

IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);

We can add our own IExternalSignature implementations wherever we need.

signer.SignDetached(pks, chain, null, null, null, 0, 
PdfSigner.CryptoStandard.CMS);

iText provides the SignDetached method to sign the PDF. It requires the private key access along with the hashing algorithm that we set in the last step. It also requires the cryto standard which we have selected CMS for now. There are more options available which I will explore in my later posts.

iText also provide another method to sign that is SignExternalContainer and there is another static method called SignDeferred. We will explore then in my next few posts one by one.

Results

Tada!! following is the screen shot of the PDF signatures that is created from the above code snippets.

Wait, what!!! “At least one signature has problems“? The good thing is that we signed the PDF document. The error is because the certificate was a self signed certificate. Adobe doesn’t trust the issuer of that certificate and rightfully so. That is why these legal identities are protected by the certificate authorities. In production we will either use a certificate that is trusted by the Adobe so that it always show a green tick after the signatures or we will add our root certificate in to the trust list of OS (Operating System) manually. I will explain that manual step in my future posts.

Sources

  • iText7 Documentation
  • iText7 Sample Codes
  • Bouncy Castle Documentation

And at the end a big shout out for the C# advent 2019, checkout the other activities related to the C# in this vibrant online advent.

3 thoughts on “PDF Digital Signatures with iText7, Bouncy Castle and .Net Core

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.