Click here to Skip to main content
Click here to Skip to main content

Digital Signatures and PDF Documents

, 30 Apr 2007
PDFKit.NET 2.0 is a 100% .NET (verifiable) component for creating and manipulating PDF documents. In this article I will focus on its digital signature capabilities. Digital signatures can be used to authenticate the source of a PDF document, and to provide the integrity of a PDF document.

Editorial Note

This article is in the Product Showcase section for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.

PDFKit.NET 2.0 is a 100% .NET (verifiable) component for creating and manipulating PDF documents. In this article I will focus on its digital signature capabilities. Digital signatures can be used to authenticate the source of a PDF document (who signed it?) and to provide the integrity of a PDF document (did the document change after it was signed?). In this article I will show how to apply one or more digital signatures and how to verify digital signatures.

Contents

Signing

Consider the following form:

Screenshot - screenshot_form_editmode.png
Figure 1: PDF Form with fields and two empty signature fields

This form has two sections of form fields; one for the student and one for the teacher. Normally, first the student will fill out his portion and sign the document. This can be achieved programmatically as follows:

using (FileStream sourceFile = new FileStream(@"form.pdf", FileMode.Open, 
   FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   // file out the data fields
   TextField projectNr = document.Fields["projectNumber"] as TextField;
   projectNr.Value = "FF-235";
   
   TextField sName = document.Fields["studentName"] as TextField;
   sName.Value = "Bob Stapleton";
   
   TextField sDate = document.Fields["studentDate"] as TextField;
   sDate.Value = "April 18, 2007";

   // retrieve the signature field
   SignatureField sigField = document.Fields["studentSignature"] as 
      SignatureField;
   
   // open certicate store
   Pkcs12Store store = null;
   using (FileStream storeFile = new FileStream(@"ChrisSharp.pfx", 
      FileMode.Open, FileAccess.Read))
   {
      store = new Pkcs12Store(storeFile, "Sample");
   }
   
   // let the factory decide which type should be used
   SignatureHandler handler = StandardSignatureHandler.Create(store);

   // associate the handler with the signature field
   sigField.SignatureHandler = handler;

   // set optional info
   sigField.ContactInfo = "+31 (0)77 4748677";
   sigField.Location = "The Netherlands";
   sigField.Reason = "I hereby declare!";

   // save the signed document - while saving, the handler 
   // will be called to sign the saved content
   using (FileStream outFile = new FileStream(@"signedByStudent.pdf", 
      FileMode.Create, FileAccess.ReadWrite))
   {
      document.Write(outFile);
   }
}

After executing the code above and opening the PDF document in the PDF reader, the document looks as follows:

Screenshot - signedByStudent.png
Figure 2: After applying the first signature

Note the question mark as shown by the PDF reader. This means that the certificate has not yet been trusted by the client machine. This is simply a matter of adding the certificate to the trust store.

Next, the teacher will review the student's data and then fill out the final fields and sign it. This can be achieved programmatically as follows:

using (FileStream sourceFile = new FileStream(@"..\..\signedByStudent.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   // file out the data fields
   TextField tName = document.Fields["teacherName"] as TextField;
   tName.Value = "Max Boulton";

   TextField tDate = document.Fields["teacherDate"] as TextField;
   tDate.Value = "April 18, 2007";

   // retrieve the signature field
   SignatureField sigField = document.Fields["teacherSignature"] as 
      SignatureField;

   // open certicate store.
   Pkcs12Store store = null;
   using (FileStream storeFile = new FileStream(@"..\..\MaxBoulton.pfx", 
      FileMode.Open, FileAccess.Read))
   {
      store = new Pkcs12Store(storeFile, "teacherpassword");
   }

   // let the factory decide which type should be used.
   SignatureHandler handler = StandardSignatureHandler.Create(store);

   // associate the handler with the signature field
   sigField.SignatureHandler = handler;

   // set optional info.
   sigField.ContactInfo = "+31 (0)77 4748677";
   sigField.Location = "The Netherlands";
   sigField.Reason = "I hereby declare!";

   // save the signed document - while saving, the handler 
   // will be called to sign the saved content
   using (FileStream outFile = new FileStream(@"..\..\signedByTeacher.pdf", 
      FileMode.Create, FileAccess.ReadWrite))
   {
      document.Write(outFile, DocumentWriteMode.AppendUpdate);
   }
}

If you look at the code, it is practically the same as the previous code sample. The only significant difference is that I pass an extra argument to the Document.Write method: DocumentWriteMode.AppendUpdate. This tells PDFKit.NET 2.0 to save all changes as a so-called Update. I will discuss this in the next section.

After executing the code above and opening the PDF document in the PDF reader, the document looks as follows:

Screenshot - signedByTeacher.png
Figure 3: After applying the second signature

Note that the icon of the first signature has changed to a warning sign. This indicates that "the document has been updated since signed". This is exactly the case.

Updates

Note that when we saved the second signature, we passed an extra argument to Document.Write, namely DocumentWriteMode.AppendUpdate. This instructs PDFKit.NET to save the new field data and the signature as an Update. This means that the original PDF data is left entirely intact and the changes are concatenated. The figure below illustrates this.

Screenshot - saveAsUpdate.png
Figure 4: PDF Updates

Consequently, the first signature remains valid because the exact data that was signed hasn't changed; we have just added an update.

So after saving the update there are now in fact two versions of the document; one that signed by the student and one that was signed by the teacher. It is useful to retrieve the exact document to which a given signature was applied. Obviously the signer only vows for that version and not for the versions that were created afterwards.

Given a document you can enumerate all updates or versions of the document and save a copy to disk as follows:

using (FileStream sourceFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the PDF document
   Document document = new Document(sourceFile);

   // count the number of updates
   Console.WriteLine("This document has {0} updates.",document.Updates.Count );
   
   // save each update as a new PDF document
   foreach (Update update in document.Updates)
   {
      string name = string.Format( @"..\..\signedByTeacher_{0}.pdf", 
         update.Index );
      using (FileStream updateFile = new FileStream(name, FileMode.Create, 
         FileAccess.Write))
      {
         update.Write(updateFile);
      }
   }
}

But perhaps even more interesting, you can open a signed document and per signature field you can retrieve the signed update. The following code sample enumerates all signature fields and saves the signed update.

using (FileStream sourceFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   foreach (Field field in document.Fields)
   {
      // is this a signature field?
      SignatureField sigField = field as SignatureField;
      if (null != sigField)
      {
         // has it been signed?
         if (sigField.IsSigned)
         {
            // save the update and name it after the field
            string name = string.Format(@"..\..\{0}.pdf", sigField.FullName);
            using (FileStream updateFile = new FileStream(name, 
               FileMode.Create, FileAccess.Write))
            {
               sigField.SignedUpdate.Write(updateFile);
            }
         }
      }
   }
}

After executing this code, two new PDF documents have been saved: studentSignature.pdf and teacherSignature.pdf. Each document shows the version that was signed by the respective field.

Verifying

Until now we have discussed signing documents. The verification was left to the PDF reader application. But PDFKit.NET also allows you to verify signatures programmatically. This is extremely simple as shown in the next code sample. The sample opens the PDF document that was signed by the student and the teacher and enumerates the signature fields. Per signature, information about the signature state is written to the console.

using (FileStream inFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open form
   Document document = new Document(inFile);

   foreach (Field field in document.Fields)
   {
      // is this a signature field?
      SignatureField sigField = field as SignatureField;
      if (null != sigField)
      {
         Console.WriteLine("Field '{0}'", sigField.FullName);

         // has it been signed?
         if (sigField.IsSigned)
         {
            // verify, based on the default handlers.
            bool verified = sigField.Verify();
            Console.WriteLine("  -- {0}", 
               verified ? "Verified" : "Not verified");

            if (verified)
            {
               // has the document been modified after signing?
               bool modified = sigField.DocumentModifiedAfterSigning;
               Console.WriteLine("  -- {0}", modified ? "Modified after 
                  signing" : "Not modified after signing");
            }
         }
         else
         {
            Console.WriteLine("  -- Not signed", sigField.FullName);
         }
      }
   }
}

After executing the above code, the following is written to the console:

Screenshot - verify.png

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Frank Rem
Web Developer
Netherlands Netherlands
Graduated in 1997. Worked for some years as a software engineer, architect and project leader for different software companies. Founded TallComponents in 2001.

Comments and Discussions

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 30 Apr 2007
Article Copyright 2007 by Frank Rem
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid