Add Digital Signatures to PDFs in Windows

PSPDFKit enables signing both existing signature form elements and documents without a signature form element. If there is no signature form field in the current document, PSPDFKit gives you the byte range and a hash representation of the current state of the document, so you can digitally sign it by obtaining a DER PKCS#7 container from either the byte range or the hash and then applying that DER PKCS#7 container to a new, ad hoc invisible signature form field.

You’re only responsible for providing the signing service that will receive the byte range and the hash as input values and return the DER PKCS#7 container to be applied to the prepared signature form field. Our implementation allows you to produce, validate, and display digitally signed documents in a totally flexible way.

ℹ️ Note: If you want to use the Digital Signatures feature, make sure to include it in your license. Please follow this link to contact sales to start using it.

Creating a Digital Signature

Adding a digital signature on a PDF document is both reliable proof of the document’s origin and protection against modification by third parties.

To create a digital signature, you need two things.

  • First, you need an X.509 certificate that contains your public key and your signer information. PSPDFKit supports PEM-encoded X.509 certificates, as well as PEM-encoded PKCS#7 certificates. You can verify if a PKCS#7 certificate file is correctly PEM-encoded by using the OpenSSL command-line tool as follows:

  openssl pkcs7 -noout -text -print_certs -in example.p7b

The above command will print an error message if “example.p7b” is not a PEM-encoded PKCS#7 certificate or certificate chain.

To verify if a PKCS#7 certificate file is correctly DER encoded, you can use this command instead:

  openssl pkcs7 -inform der -noout -text -print_certs -in example.p7b

The above command will print an error message if “example.p7b” is not a DER-encoded PKCS#7 certificate or certificate chain.

  • Second, you need your private key. A self-signed private key and certificate pair can be created with the command shown in the previous section.

Single-Step or Two-Step Signing

With PSPDFKit for Windows, there are two strategies for digitally signing documents. You can provide the certificate and private key in PEM formats and sign the document in a single step. Alternatively, there’s a two-step process where you can register an event handler that gets called by the framework to retrieve the signature. In that handler, you can generate the signature yourself and provide that to PSPDFKit for Windows. The two-step process can be useful for workflows where you may wish to perform the signing with specialized hardware devices or sign remotely, etc. It also has the benefit of ensuring that PSPDFKit doesn’t need access to the private key that ultimately will be used to produce the signature value, leaving you complete freedom to choose which strategy to use to manage its lifecycle and security.

How It Works

Under the hood, the process of signing a document is divided into three phases:

  1. PSPDFKit prepares the document for a signature, adding an invisible form field that will contain the signature value.

  2. PSPDFKit then either generates the signature in a PKCS#7 container internally or fires the two-step signature signing event handler for you to generate the signature.

  3. PSPDFKit applies the signature and saves the document.

If the signing process is successful, the document is reloaded with the new invisible digital signature added to it.

During the call to Document.SignAndSaveAsync, and until either the DER PKCS#7 container is generated and PSPDFKit applies the signature to the document or the process is disregarded due to a rejection from the callback, all UI interactions from the user are disabled. This ensures that no modifications are made to the document while a new digital signature is about to be added to it.

Single-Step Signing Example

// PSPDFKit for Windows supports certificates and private keys in PEM format.
var certificate = await GetTestAssetTextAsync("certificate.pem");
var privateKey = await GetTestAssetTextAsync("private-key.pem");

var doc = await Document.OpenDocumentAsync(source);

// Sign the document. SHA256 or greater is recommended.
await doc.SignAndSaveAsync(HashAlgorithm.Sha256, certificate, privateKey, new SignatureMetadata
            {
                SignerName = "James Swift",
                SignatureReason = "Example Signature",
                SignatureLocation = "Vienna, Austria"
            });

Two-Step Signing Example

// PSPDFKit for Windows supports certificates and private keys in PEM format.
var certificate = await GetTestAssetTextAsync("certificate.pem");
var privateKey = await GetTestAssetTextAsync("private-key.pem");

var doc = await Document.OpenDocumentAsync(source);

// Register the two-step signature signer event handler.
doc.TwoStepSignatureSigning += async (deferral, signingData) =>
{
    var certificate = GetCertificate();
    var privateKey = GetPrivateKey();

    // Hash the PDF data. SHA256 or greater is recommended.
    byte[] hash;
    using (var sha256 = new SHA256CryptoServiceProvider())
    {
        hash = sha256.ComputeHash(signingData.FileContents.AsStreamForRead());
    }

    // You can compare your own hash with a hash generated by PSPDFKit from the same data using the specified algorithm.
    if (CryptographicBuffer.EncodeToHexString(hash.AsBuffer()).ToUpperInvariant() != signingData.Hash)
    {
        throw new Exception("Hashes don't match.");
    }

    try
    {
        // Sign the hash.
        // For this example, we use a utility method provided by PSPDFKit, but you can use your own library
        // if you need to ensure that PSPDFKit doesn’t have access to the private key.
        var signature = await Utilities.GeneratePKCS7ContainerAsync(
            hash.AsBuffer(),
            certificate,
            privateKey,
            HashAlgorithm.Sha256);

        signingData.Pkcs7Container = signature;
    }
    finally
    {
        // It is essential to complete the deferral.
        // Read more about `Deferral`s in this blog post:
        // https://pspdfkit.com/blog/2019/async-callbacks-with-deferral-csharp/
        deferral.Complete();
    }
};

// Sign the document. SHA256 or greater is recommended.
// No need to provide a certificate or private key.
await doc.SignAndSaveAsync(HashAlgorithm.Sha256, "", "", new SignatureMetadata
            {
                SignerName = "James Swift",
                SignatureReason = "Example Signature",
                SignatureLocation = "Vienna, Austria"
            });