Digital signatures
Version 6.2.0 · 2024-08-12
In this article
- What are digital signatures?
- How it works
- Visual representation of the signature
- Sign a new document
- Sign an existing document
- Adding a timestamp
- Determining the signature size
- Re-use IDigitalSigner
- Using Bouncy Castle
- Creating a PKCS certificate
- Working with certificates
This article describes how to add a digital signature to a PDF document. This feature is introduced with PDFsharp 6.2.0 and based on work done by the PDFsharp community. Big thanks to all contributors.
What are digital signatures?
A digital signature proves the integrity of a PDF document. To accomplish this, a digest of the data in the document is computed and stored within the document. You can think of this digest as a secure kind of checksum that is calculated including all bytes of the document except the bytes that contain the digest. The digest itself is protected against modification by signing it with a certificate and an optional timestamp.
Digital signatures are a complex topic and we recommend that you are familiar with it. As a reference you should read the chapter Digital Signatures in the original PDF Reference 1.7 or 2.0.
How it works
The PDF specification describes several ways to provide a PDF file with a digital signature. PDFsharp supports digital signatures based on Cryptographic Message Syntax (CMS).
Prerequisites
To create a digital signature, you need a so-called digital signature handler. It gets the bytes of the document as input and returns the digital signature. A simple but useful example of a digital signature handler is one that takes a PKCS7 certificate to calculate the signature. A more robust way from the perspective of security is a hardware device that calculates the signature.
With PDFsharp, you can write your own digital signer by implementing the interface IDigitalSigner. PDFsharp comes with the built-in signer PdfSharpDefaultSigner. It basically takes an X509Certificate2 object to produce the signature.
You can create your own certificate with e.g. OpenSSL. For demonstration purposes, PDFsharp comes with some sample certificates we use in our own tests and sample code.
Note that PdfSharpDefaultSigner resides in the new assembly PdfSharp.Cryptography
.
We do so to prevent making the PDFsharp assembly dependent on System.Security.Cryptography.Pkcs
.
This was more a political than a technical decision.
Customers that do not need digital signatures often include PDFsharp in their own products and setup software.
A dependency on System.Security.Cryptography.Pkcs
(which is not part of the .NET runtime) implicates that this assembly must be included e.g. in the setup software.
Less technical affine project members may be concerned by words like 'cryptography' or 'PKCS'.
To prevent pointless discussions from the beginning, we put the default signer in a separate assembly.
Furthermore, if you write your own signer, you don’t need it anyway.
Visual representation of the signature
The visual representation of the signature in the PDF is a PdfSignatureField and you use PDFsharp code to create an XForm with the graphics. This can be a simple string, a scanned signature or any other image, lines and shapes, or a mix of all this. The person reading the PDF can click on this visual representation to see details about the signature including the certificate and an optional timestamp.
Sign a new document
When creating a new document, it is easy to integrate the signature at a suitable position.
PDFsharp uses co-ordinates relative to the top-left corner of the page. For the signature field, these co-ordinates must be transformed.
var pdfPosition = xGraphics.Transformer.WorldToDefaultPage(new XPoint(144, 600));
Additionally you need to specify options for the signature:
var options = new DigitalSignatureOptions
{
ContactInfo = "John Doe",
Location = "Seattle",
Reason = "License Agreement",
Rectangle = new XRect(pdfPosition.X, pdfPosition.Y, 200, 50),
AppearanceHandler = new SignatureAppearanceHandler()
};
The options specify the position of the signature representation along with some text fields stored in the PDF.
And now associate the options with your document:
var pdfSignatureHandler = DigitalSignatureHandler.ForDocument(document,
new PdfSharpDefaultSigner(GetCertificate(), PdfMessageDigestType.SHA256),
options);
See the DefaultSigner sample for the implementations of SignatureAppearanceHandler and GetCertificate().
Sign an existing document
Signing an existing document is basically the same as signing a new document. One important difference is that your document is the result of reading a file or a stream. Another major difference is that you must know the document if you want to add the signature on an existing page, maybe in the margin area. You can always play it safe and add an empty page at the end and put the signature on that new page.
Adding a timestamp
Adding a timestamp is easy as you only have to add the URI of the timestamp server as the third parameter to the constructor of PdfSharpDefaultSigner. Note that timestamped signatures are not supported by the .NET Framework build of PDFsharp. Specifying a timestamp server with the Framework build will result in an exception.
Determining the signature size
The size of a signature depends on many details, e.g. the type of the used hash algorithm, the size of your PKCS certificate, and, if you add a timestamp, the type of the timestamp server and the size of its certificate. But it does not depend on the size of the document.
When PDFsharp prepares a document for signing, it must preserve space for the signature before the signature is calculated. To get the size, PDFsharp calculates a signature for a one byte dummy document. In case you use a timestamp, this includes an async call to the timestamp server. Therefore, IDigitalSigner has two async functions.
When you create a signed document with a timestamp for the signature, two roundtrips to the timestamp server are executed when you use the PdfSharpDefaultSigner. If you want to prevent this additional roundtrip, write your own digital signer. Because in your specific use case your certificate and timestamp server is well known and you can simply try out which size the signature has. Add some bytes to the size because the total certificate size may vary depending on the actual time.
Re-use IDigitalSigner
When signing the first document with an instance of IDigitalSigner, a digital signature is created for a dummy object in order to determine the size of the digital signature. If timestamping is active, this includes a roundtrip to the timestamp server.
If an instance of IDigitalSigner is used to sign multiple documents, the extra roundtrip to the timestamp server is made only once.
Using Bouncy Castle
The BouncyCastleSigner is an implementation of IDigitalSigner using the Bouncy Castle cryptographic library. Bouncy Castle is one of the most widely used FIPS-certified open-source cryptographic APIs for Java and C#.
Bouncy Castle’s FIPS-certified Cryptographic API offers a high level of assurance regarding the security and reliability of cryptographic operations. Previously certified to FIPS 140-2, the Bouncy Castle APIs now adhere to the stringent guidelines outlined in the FIPS 140-3 standard which is recognized internationally for its rigorous testing and validation procedures.
The BouncyCastleSigner is not part of the PDFsharp NuGet packages. You can find it in the code snippets in the PDFsharp repository on GitHub.
Creating a PKCS certificate
You can easily create your own certificate with OpenSSL.
See file docs\Signatures\CertificateCreation.md
in the source code repository.
Working with certificates
Most signature samples use a certificate in a .PFX file and the password for the certificate file is written in plain text in the source code. This is the simple approach for sample code.
Do not write passwords for real certificates into your source code. Protect your certificates. Protect your private key. We neglect security with our fake certificates. But not with our real certificates.
One alternative under Windows: Install the certificate into the certificate store.
Then you can access the certificate from your code by name and sign documents without providing a password.
The unit test Sign_with_Certificate_from_Store()
in the PDFsharp repository demonstrates this technique.
This unit test will be skipped on your computer if no suitable certificate can be found.