A digital signature protects a document's integrity and
provides proof of the signer's identity.
AspPDF.NET is capable of adding a X.509 certificate-based
digital signature to a PDF document in PKCS#7 format
via the Sign method of the PdfDocument object.
8.3.1 Support for Certificate-based Cryptography in .NET
To create a digitally signed document, or sign an existing
PDF, AspPDF.NET needs to obtain an instance of the X509Certificate2
object (System.Security.Cryptography.X509Certificates namespace.)
Certificates usually reside in "certificate stores". The following code snippet
obtains a certificate by its subject name from the "ROOT" certificate store
residing in the "local machine" section of the server's storage:
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
...
X509Store objStoreMy = new X509Store( StoreName.Root, StoreLocation.LocalMachine );
objStoreMy.Open(OpenFlags.ReadOnly);
X509Certificate2Collection objCertColl =
objStoreMy.Certificates.Find( X509FindType.FindBySubjectName,
"Persits Software", false );
X509Certificate2 objCert = objCertColl[0];
The objCert object thus obtained can now be passed directly to the PdfDocument.Sign method
for AspPDF.NET to perform the digital signing of the document.
Digital signing is often a tricky process as it involves private keys that
are inherently secure entities.
The code shown above, when run in an ASP.NET environment, will not produce an error but an
attempt to use the certificate
in the Sign method probably will, as we are now trying to access the private key and causing
a security violation. To avoid the error, "user impersonation" may be required.
The PdfManager object offers the LogonUser method
which impersonates an arbitrary user account. The method expects
the domain name (or an empty string if this is a local domain), username and password as arguments.
Call this method before calling the certificate-store methods,
as follows:
objPDF.LogonUser( "", "Administrator", "MyPassword" );
X509Store objStoreMy = new X509Store( StoreName.Root, StoreLocation.LocalMachine );
...
The signer certificate does not have to reside in a certificate store, it can also be
obtained directly from a password-protected .pfx file (PKCS#12 format).
An instance of the X509Certificate2 object is created by calling its constructor
and passing the .pfx file path and password as arguments.
Note that in a ASP.NET environment you must call LogonUser before creating the certificate object
this way, or the signing will fail. For example:
objPDF.LogonUser( "", "Administrator", "MyPassword" );
X509Certificate2 objCert = new X509Certificate2( @"c:\path\mycert.pfx", "my_password" );
...
For more general information about X509 certificates, visit the AspEncrypt.com site.
8.3.2 Sign Method
The Sign method expects a instance of the X509Certificate2
object with its HasPrivateKey property set to true.
It also accepts the signer name, reason for signing, and location
arguments (the first one is required, the other two are optional),
and also an optional parameter string or parameter object controlling the
visibility and location
of the signature field within the document.
The following code sample adds a signature to a PDF document:
objDoc.Sign( Cert, "John Smith", "I created this document.", "New York, NY" );
It is recommended that Acrobat Reader 6.0+ be used to view digitally signed documents.
Acrobat 5.0 (the full version, not the Reader)
is also acceptable, but you must also download and install the
VeriSign Document Signer
plug-in, and only signatures based on VeriSign certificates seem
to be recognized as valid by this plug-in. A free 60-day
VeriSign certificate can be obtained here.
NOTE: A digitally signed document can only be saved to disk (via the Save method).
Saving to memory or an HTTP stream cannot be used once Sign
is called, and an attempt to call SaveToMemory or SaveHttp will result
in an error exception.
UPDATE: As of Version 2.7, a signed document
can be saved to disk and memory, but not to an HTTP stream.
8.3.3 Visible Signatures
By default, the Sign method creates an invisible signature.
Using the last optional argument of the Sign method, it is possible
to specify a page and location within that page where the signature
icon is to appear.
The following code fragment draws a signature icon in the upper-left
corner of the first page of the document (middle arguments omitted for brevity):
objDoc.Sign( Cert, ..., "visible=true;x=10,y=750;width=20;height=20;pageindex=1" );
It is also possible to change the default appearance of a signature
by drawing on the canvas of a PdfAnnot object
returned by the Sign method. Annotations are described in detail
in Chapter 10.
8.3.4 Signature Validation
AspPDF.NET's digital signature functionality would not be complete without
a way to verify an existing signature in a document.
Signature verification is implemented via the VerifySignature method
of the PdfDocument object.
VerifySignature can only be called on an instance of PdfDocument
created via the file version of OpenDocument, not the byte array version.
VerifySignature does not take any arguments.
VerifySignature returns null if no PKCS#7 signatures
are found in the document. Otherwise, it returns an instance
of the PdfSignature object
encapsulating various property of the signature, including
its validation status (valid/invalid), signer name, reason for signing,
location, and other information. If a document contains multiple signatures (such as, when
an already signed document was signed again), the VerifySignature method
validates and returns the one that covers the largest portion of the document,
which is usually the latest signature.
The following code fragment opens a PDF document from a file,
and attempts to validate a signature, if one is present:
PdfDocument objDoc = objPDF.OpenDocument(@"c:\somefile.pdf");
PdfSignature objSig = objDoc.VerifySignature();
if( objSig == null )
{
Console.WriteLine( "No signature found." );
}
else
{
Console.WriteLine( "Status = " + objSig.Status );
Console.WriteLine( "Name = " + objSig.Name );
Console.WriteLine( "Reason = " + objSig.Reason );
Console.WriteLine( "Contents = " + objSig.Contents );
}
Server-side digital signing is demonstrated by Live Demo #17:
https://www.support.persits.com/pdf_net/demo_sign.cs.aspx