Click or drag to resize
Pdf Library for .NET

Digital Signatures

Digital signatures can be added to a PDF document using PdfDigitalSignatureElement objects. Adding a digital signature requires a PKCS#12 certificate (provided as a .pfx or a .p12 file).

A certificate read from a file carries its private key, which the operating system keeps until the certificate is released - on Windows as a key file in the user profile, on macOS in a temporary keychain. Dispose of the PdfDigitalCertificatesCollection (and of a PdfCertificateStoreSigner) with a using statement, AFTER the documents it signs are saved: a document is signed when it is saved, and a certificate disposed before that fails the save. PdfSecurityManager releases the certificates its Sign loads when it is closed or disposed.

Long-term validation (LTV)

A signature can be validated only as long as its certificate can be checked: once the certificate has expired, or the issuer's revocation services are gone, a validator has nothing to go on. Set EnableLtv (or EnableLtv before Sign) and the revocation data of the signing certificate and of its issuers - an OCSP response or a certificate revocation list for each, from the addresses the certificates name - is embedded in the document when it is saved, together with the issuer certificates (PAdES B-LT). The signature can then be validated offline, years later.

The data is added in a revision of its own after the signature, so the signature still covers everything it signed, and each signer of a document signed in turn adds the data for their own signature. The issuer certificates are taken from the certificate file the signing certificate came from and from the system certificate stores. Saving fails when a certificate of the chain names no OCSP responder or revocation list, or when they cannot be reached, so a document is never handed back without the data it was asked to carry. IsLtvEnabled reports whether a signature has it.

using PdfDigitalCertificatesCollection certs =
    PdfDigitalCertificatesStore.GetCertificates("signer.pfx", "password");
PdfDigitalCertificate signer = certs.GetCertBySubject("Contract Signer");

using PdfDocument document = new PdfDocument("contract.pdf");
PdfDigitalSignatureElement signature = new PdfDigitalSignatureElement(
    new SelectPdf.Universal.Drawing.RectangleF(40, 600, 240, 70), signer);
signature.Reason = "Approved";
signature.EnableLtv = true;
document.Pages[0].Add(signature);
document.Save("contract-signed.pdf");
Signing a form's signature field

A form often arrives with an empty signature field - a "sign here" box. Pass that field's name to Sign and the signature goes into it, on the field's own page and in its own rectangle (the page index and rectangle given to Sign are then not used). A field that is already signed is never overwritten: a new signature with the same name gets a field of its own.

PdfSecurityManager manager = new PdfSecurityManager();
manager.Load("form.pdf");
manager.Sign("signer.pfx", "password", "Approver",
    "Approved", "Bucharest", "office@example.com");
manager.Save("form-signed.pdf");
manager.Close();
Hash algorithm and key types

Signatures are computed with SHA-256 by default. Set DigestAlgorithm (or DigestAlgorithm before Sign) to SHA384 or SHA512 when a signature policy asks for them. Both RSA and elliptic-curve (ECDSA) certificates can sign; SHA-384 is the usual pairing for a P-384 key.

PdfDigitalSignatureElement signature = new PdfDigitalSignatureElement(
    new SelectPdf.Universal.Drawing.RectangleF(40, 600, 240, 70), signer);
signature.DigestAlgorithm = PdfSignatureDigest.SHA384;
document.Pages[0].Add(signature);
Signing with a smart card, a USB token or an HSM

Two ready-made signers cover keys that never leave a device. PdfCertificateStoreSigner signs with a certificate from the system certificate store, found by its thumbprint - which is where the middleware of a smart card or USB token registers the device's certificate on Windows. The device signs and asks for its PIN itself. PdfPkcs11Signer talks to the device directly through its vendor's PKCS#11 library: an HSM, a token on a server, or any device on Linux and macOS. It reads the signing certificate from the token, logs in with the PIN and keeps its session open until it is disposed, so one signer signs any number of documents. Both find the issuer certificates and embed them, and both work with EnableLtv, a time-stamp authority and the visible signature element.

// A certificate on a USB token, as the certificate manager lists it.
using (var storeSigner = new PdfCertificateStoreSigner(
    "BABEA413778B70C92E266D2458E193BD6E62A6E3"))
using (var manager = new PdfSecurityManager())
{
    manager.Load("contract.pdf");
    manager.Sign(storeSigner, "Signature1", "Approved", "Bucharest", "",
        0, new RectangleF(40, 120, 260, 70));
    manager.Save("contract-signed.pdf");
}

// The same token, or an HSM, through the vendor's PKCS#11 library.
using (var pkcs11Signer = new PdfPkcs11Signer(
    @"C:\Windows\System32\eTPKCS11.dll", "1234", "My Token", "Signing key"))
{
    PdfDocument doc = new PdfDocument("invoice.pdf");
    doc.Pages[0].Add(new PdfDigitalSignatureElement(
        new RectangleF(40, 700, 260, 70), pkcs11Signer));
    doc.Save("invoice-signed.pdf");
    doc.Close();
}

The PKCS#11 library must match the application's processor: a 64-bit application needs the vendor's 64-bit library. With no token label exactly one token must be present, and with no key label the token must hold exactly one private key - the signer never tries the PIN on a token it was not pointed at, since a wrong PIN counts against the token's retry limit.

The visible signature's appearance

A visible signature shows the signer's name large on the left and, on the right, "Digitally signed by", the name, the reason and location when given, and the date with its UTC offset. Behind the text a faint red handwritten flourish is drawn. Replace it with your own image - a company logo, a seal, a scanned handwritten signature - with SignatureBackgroundImage (the same property exists on PdfSecurityManager), and set how strongly it is drawn with SignatureBackgroundOpacity, from 0 (no background) to 1 (the image's own colours; default 0.25). The background is lightened rather than made transparent, so it is allowed in every PDF/A and PDF/X document.

var signature = new PdfDigitalSignatureElement(
    new RectangleF(40, 700, 260, 70), certificate);
signature.SignatureBackgroundImage = File.ReadAllBytes("logo.png");
signature.SignatureBackgroundOpacity = 0.3f;
page.Add(signature);
Signing with a key held outside: HSMs, smart cards and key services

When the private key lives in a hardware security module, a smart card, a PKCS#11 token or a cloud key service, it cannot be handed to the library as a certificate file. Implement IPdfExternalSigner instead: it gives the signer's certificates (DER-encoded, the signing certificate first, then its issuers) and signs one PdfExternalSigningRequest. The library builds the whole signature - certificates, signed attributes, timestamp, long-term validation data - and asks the signer only for the raw signature value, so the key never leaves the device or service. Pass the signer to Sign or to a PdfDigitalSignatureElement.

The request carries the data to sign and its digest: sign the data with a service that hashes it itself, or the digest with one that expects a hash. An RSA key returns a PKCS#1 v1.5 signature (RSA-PSS is not supported); an ECDSA key may return the DER-encoded signature or r and s concatenated, the form .NET and most key services produce. The value is checked against the signing certificate before it is written, so a value from another key or another hash algorithm makes the save fail instead of producing a document that looks signed and does not verify. DigestAlgorithm, EnableLtv, a time-stamp authority and signing a form's named field work as they do with a certificate file.

// Any key that can sign a digest: here a .NET RSA object, which HSM
// and key service SDKs commonly provide.
class KeySigner : IPdfExternalSigner
{
    private readonly RSA key;

    public KeySigner(IList<byte[]> certificates, RSA key)
    {
        Certificates = certificates;
        this.key = key;
    }

    public IList<byte[]> Certificates { get; }

    public byte[] Sign(PdfExternalSigningRequest request)
    {
        var hash = new HashAlgorithmName(request.DigestAlgorithm.ToString());
        return key.SignHash(request.Digest, hash, RSASignaturePadding.Pkcs1);
    }
}

PdfSecurityManager manager = new PdfSecurityManager();
manager.Load("contract.pdf");
manager.Sign(new KeySigner(certificates, key), "Signature1");
manager.Save("contract-signed.pdf");
manager.Close();

Signing in two steps. A remote signing service where the signer approves on another device, or a smart card in a user's browser, cannot answer while the document is being saved. PrepareExternalSignature gives the document a signature field with an empty, reserved signature and returns the request; save the prepared document as it is, have the request signed, then CompleteExternalSignature writes the value into it, with the same signature name, certificates and hash algorithm. The two steps can run in different processes, hours apart. The completed document is validated before it is returned. Long-term validation and a time-stamp authority are not available in this mode.

// step one: reserve the signature and hand out what must be signed
PdfSecurityManager manager = new PdfSecurityManager();
manager.Load("contract.pdf");
PdfExternalSigningRequest request =
    manager.PrepareExternalSignature(certificates, "Signature1");
manager.Save("contract-prepared.pdf");
manager.Close();

byte[] value = remoteService.SignDigest(request.Digest);

// step two, later: write the value into the prepared document
PdfSecurityManager.CompleteExternalSignature("contract-prepared.pdf",
    "contract-signed.pdf", "Signature1", certificates,
    PdfSignatureDigest.SHA256, value);
Validating signatures

ValidateSignatures checks every signature of a loaded document and returns a PdfSignatureValidationResult for each: whether it is intact (IsValid), whether its certificate chains to a trusted root (IsTrusted), whether content was added to the document after it (CoversWholeDocument), the signing and time-stamp times, the signer and the certificate, and a Status that sums them up. Pass your own certificate authority to trust it as a root, and set RevocationCheckEnabled to ask the certificate's issuer whether it has been revoked.

Each signature is checked against the bytes it was made over, so a signature followed by a later one, or by a form fill, is reported intact.

With the revocation check on, the revocation data a document carries for long-term validation is used first, so an LTV-enabled signature is checked without going online. An OCSP response or revocation list counts only when the certificate's issuer (or a responder it authorised) signed it.

PdfSecurityManager manager = new PdfSecurityManager();
manager.Load("contract-signed.pdf");
manager.RevocationCheckEnabled = true;

// trust the organisation's own certificate authority as well as the system's
using PdfDigitalCertificatesCollection roots =
    PdfDigitalCertificatesStore.GetCertificates("our-ca.pfx", "password");
foreach (PdfSignatureValidationResult result in manager.ValidateSignatures(roots))
{
    Console.WriteLine("{0}: {1}, signed by {2} on {3}",
        result.SignatureName, result.Status, result.SignerName, result.SigningTime);
    foreach (string message in result.Messages)
        Console.WriteLine("  " + message);
}
manager.Close();
Sample Code

The following sample code shows how to add a digital signature to a pdf document using Select.Pdf Library for .NET.

// create a new pdf document
PdfDocument doc = new PdfDocument();

// add a new page to the document
PdfPage page = doc.AddPage();

// get image path 
// the image will be used to display the digital signature over it
string imgFile = @"C:\Path\to\logo.png";

// get certificate path
string certFile = @"C:\Path\to\certificate.pfx";

// define a rendering result object
PdfRenderingResult result;

// create image element from file path 
PdfImageElement img = new PdfImageElement(0, 0, imgFile);
result = page.Add(img);

// get the #PKCS12 certificate from file; dispose of it after the save,
// since the document is signed when it is saved
using PdfDigitalCertificatesCollection certificates =
    PdfDigitalCertificatesStore.GetCertificates(certFile, "selectpdf");
PdfDigitalCertificate certificate = certificates[0];

// create the digital signature object
PdfDigitalSignatureElement signature = 
    new PdfDigitalSignatureElement(result.PdfPageLastRectangle, certificate);
signature.Reason = "SelectPdf";
signature.ContactInfo = "SelectPdf";
signature.Location = "SelectPdf";
page.Add(signature);

// save pdf document
doc.Save("Sample.pdf");

// close pdf document
doc.Close();
See Also