How to Digitally Sign PDFs with C# Using HSM
IronPDF enables secure PDF signing using Hardware Security Modules (HSMs) through the PKCS#11 API, where private keys never leave the physical device, providing bank-level security for mission-critical applications requiring tamper-proof digital signatures.
Quickstart: Sign a PDF with HSM in C#-
Install IronPDF via NuGet:
Install-Package IronPdf -
Configure your HSM device (or use SoftHSM for testing)
-
Create a
UsbPkcs11HsmSignerwith your HSM credentials:-
1Install IronPDF with NuGet Package Manager
-
2Copy and run this code snippet.
var hsmSigner = new UsbPkcs11HsmSigner(libraryPath, pin, tokenLabel, keyLabel);C# -
3Deploy to test on your live environment
Start using IronPDF in your project today with a free trial
-
-
Generate your PDF and sign it:
var pdf = renderer.RenderHtmlAsPdf("<h1>Document</h1>"); pdf.SignAndSave("signed.pdf", hsmSigner);Dim pdf = renderer.RenderHtmlAsPdf("<h1>Document</h1>") pdf.SignAndSave("signed.pdf", hsmSigner) -
Verify the signature in your PDF viewer
Adding a signature to a PDF document is a common requirement in many applications. However, mission-critical applications require higher security where the key cannot be tampered with. A normal signing operation with a .pfx file is like having a master key at your house. The application loads the key into memory to sign the document. If the computer is compromised, the key may be stolen.
A more secure alternative is using a Hardware Security Module (HSM). With an HSM (like a USB token), the private key is generated inside the device and cannot leave it.
This process is like bringing the document to a bank. The application provides a PIN, and the HSM takes the document to the vault, stamps it with the key, and returns the stamped document. The key never leaves the vault. This provides additional security, as the key cannot be copied or stolen.
How to digitally Sign PDFs with HSM
- Download the IronPDF C# library for signing PDFs with HSM
- Import your existing HSM or simulate it with other libraries
- Create a new
UsbPkcs11HsmSignerobject - Sign and save the PDF with
SignAndSave - Verify the PDF output and certificate with a PDF viewer
How Do I Sign PDFs with an HSM?
Signing with an HSM typically requires a physical device, such as a USB token, that the application interacts with. IronPDF is compatible with these operations, as both the library and standard HSMs use PKCS#11 as a common API. For demonstrative purposes, this guide uses a simulated HSM instead of a physical one.
In production or live testing environments, you should not use this simulation. Instead, use your actual HSM. For production environments, consider implementing additional PDF security features such as password protection and permissions alongside HSM signing for comprehensive document protection.
To run this simulation, you must first install SoftHSM, OpenSSL, and OpenSC to generate the necessary key and token. For more information on utilizing SoftHSM, refer to their public GitHub repository.
Before implementing HSM signing, ensure you have properly installed IronPDF and configured your license key for production use.
Start by creating a PDF from an HTML string. In the example below, we define the paths and credentials for our simulated SoftHSM. This includes providing the absolute path to the SoftHSM .dll library file and the .crt certificate file that you created.
Next, specify the output path, which in this instance is output.pdf.
Define three strings: hsmTokenLabel, hsmPin, and hsmKeyLabel. These strings are case-sensitive and must exactly match the credentials you created when generating the token and certificate with SoftHSM. Afterwards, initialize the UsbPkcs11HsmSigner object, passing the SoftHSM library path, PIN, token label, and key label as parameters.
Additionally, create a PdfSignatureImage to add a visual representation of the signature onto the document. Finally, call SignAndSave, which uses the hsmSigner to sign the document and save it to the specified output path.
What Does the HSM Signing Code Look Like?
using IronPdf;
using IronPdf.Signing;
using IronSoftware.Pdfium.Signing;
using System.Drawing;
ChromePdfRenderer renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Testing</h1>");
// Define Paths and Credentials
string softhsmLibraryPath = @"D:\SoftHSM2\lib\softhsm2-x64.dll";
// These MUST match what you created
string hsmTokenLabel = "MyTestToken";
string hsmPin = "123456";
string hsmKeyLabel = "my-key"; // The label for the key *inside* the token
// Create the HsmSigner object.
UsbPkcs11HsmSigner hsmSigner = new UsbPkcs11HsmSigner(
softhsmLibraryPath,
hsmPin,
hsmTokenLabel,
hsmKeyLabel
);
// Create the Signature Image
string signatureImagePath = "IronSoftware.png";
PdfSignatureImage sigImage = new PdfSignatureImage(signatureImagePath, 0, new Rectangle(50, 50, 150, 150));
// Sign PDF with HSM
pdf.SignAndSave("signedWithHSM.pdf", hsmSigner);Imports IronPdf
Imports IronPdf.Signing
Imports IronSoftware.Pdfium.Signing
Imports System.Drawing
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf("<h1>Testing</h1>")
' Define Paths and Credentials
Dim softhsmLibraryPath As String = "D:\SoftHSM2\lib\softhsm2-x64.dll"
' These MUST match what you created
Dim hsmTokenLabel As String = "MyTestToken"
Dim hsmPin As String = "123456"
Dim hsmKeyLabel As String = "my-key" ' The label for the key *inside* the token
' Create the HsmSigner object.
Dim hsmSigner As New UsbPkcs11HsmSigner(softhsmLibraryPath, hsmPin, hsmTokenLabel, hsmKeyLabel)
' Create the Signature Image
Dim signatureImagePath As String = "IronSoftware.png"
Dim sigImage As New PdfSignatureImage(signatureImagePath, 0, New Rectangle(50, 50, 150, 150))
' Sign PDF with HSM
pdf.SignAndSave("signedWithHSM.pdf", hsmSigner)The UsbPkcs11HsmSigner additionally takes two optional parameters: digestAlgorithm and signingAlgorithm. By default, they are set to SHA256 and RSA.
Working with Different HSM Configurations
Different HSM devices may require specific configurations. Here's an example showing how to configure the signer with custom algorithms and handle multiple signatures:
// Configure with custom algorithms
var customHsmSigner = new UsbPkcs11HsmSigner(
hsmLibraryPath,
hsmPin,
hsmTokenLabel,
hsmKeyLabel,
digestAlgorithm: IronPdf.Signing.DigestAlgorithm.SHA512,
signingAlgorithm: IronPdf.Signing.SigningAlgorithm.RSA
);
// Apply signature with custom location and reason
var signatureOptions = new SignatureOptions
{
SignerName = "Corporate Signing Authority",
Location = "Company Headquarters",
Reason = "Contract Approval"
};
// Load existing PDF for signing
var existingPdf = PdfDocument.FromFile("contract.pdf");
existingPdf.SignAndSave("contract-signed.pdf", customHsmSigner, signatureOptions);Imports IronPdf
' Configure with custom algorithms
Dim customHsmSigner As New UsbPkcs11HsmSigner(
hsmLibraryPath,
hsmPin,
hsmTokenLabel,
hsmKeyLabel,
digestAlgorithm:=IronPdf.Signing.DigestAlgorithm.SHA512,
signingAlgorithm:=IronPdf.Signing.SigningAlgorithm.RSA
)
' Apply signature with custom location and reason
Dim signatureOptions As New SignatureOptions With {
.SignerName = "Corporate Signing Authority",
.Location = "Company Headquarters",
.Reason = "Contract Approval"
}
' Load existing PDF for signing
Dim existingPdf As PdfDocument = PdfDocument.FromFile("contract.pdf")
existingPdf.SignAndSave("contract-signed.pdf", customHsmSigner, signatureOptions)This approach is particularly useful when working with digital signature examples that require specific compliance standards or when you need to add metadata to track signature details.
How Do I Add a Trusted Timestamp to an HSM Signature?
A signature records who signed a document. A trusted timestamp records when, independently of the signing machine's clock. Set TimeStampUrl on the signer to the endpoint of an RFC 3161 Time Stamp Authority and the resulting signature carries a timestamp token that conforming readers such as Adobe Acrobat recognize. This matters for long-term validation and for compliance regimes that require a verifiable signing time.
using IronPdf;
using IronPdf.Signing;
using IronSoftware.Pdfium.Signing;
ChromePdfRenderer renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Timestamped Document</h1>");
// Define Paths and Credentials
string softhsmLibraryPath = @"D:\SoftHSM2\lib\softhsm2-x64.dll";
// These MUST match what you created
string hsmTokenLabel = "MyTestToken";
string hsmPin = "123456";
string hsmKeyLabel = "my-key"; // The label for the key *inside* the token
// Create the HsmSigner object.
UsbPkcs11HsmSigner hsmSigner = new UsbPkcs11HsmSigner(
softhsmLibraryPath,
hsmPin,
hsmTokenLabel,
hsmKeyLabel
);
// Point the signer at an RFC 3161 Time Stamp Authority. The endpoint is a string.
hsmSigner.TimeStampUrl = "https://freetsa.org/tsr";
// Optional. Selects the digest the TSA is asked to use.
hsmSigner.TimestampHashAlgorithm = TimestampHashAlgorithms.SHA512;
// SignAndSave is unchanged - the timestamp is configured on the signer beforehand.
pdf.SignAndSave("signedWithTimestamp.pdf", hsmSigner);
TimeStampUrl takes the endpoint as a string. Leaving it null, empty, or whitespace skips timestamping altogether: no call is made to a TSA and signing behaves exactly as it does without one.
TimestampHashAlgorithm selects the digest the TSA is asked to produce, and accepts SHA1, SHA256, or SHA512. It is ignored when TimeStampUrl is not set.
digestAlgorithm constructor parameter described earlier. digestAlgorithm governs the signature itself, while TimestampHashAlgorithm governs only the timestamp token requested from the authority. Both happen to default to SHA-256 on the built-in signer, so it is easy to mistake one for the other.AHsmSigner, the base class behind UsbPkcs11HsmSigner. A custom signer implementing IHsmSigner directly, without deriving from AHsmSigner, gets the uninitialized enum value instead - which is SHA1, an imprint a growing number of TSA servers reject outright. Set the property explicitly in custom implementations.SignAndSave is unchanged. The timestamp is configured on the signer beforehand rather than passed as an argument, so existing calls continue to work untouched.
The IronSuite play a crucial role in our operations. These are tools that increase efficiencies across the business including creating floor plans and improving inventory management.
What Are Common HSM Configuration Issues?
If you encounter the error shown below while running the code example, follow these troubleshooting steps to debug and verify your configuration. For additional assistance with digital signature issues, consult our digital signatures troubleshooting guide.
This CKR_GENERAL_ERROR commonly occurs when the program cannot find the SoftHSM configuration file or when the .NET application is running as a 32-bit process while the SoftHSM library is 64-bit.
Changing the Platform Target
A common cause for this error is an architecture mismatch. Your C# application must run as a 64-bit process to match the 64-bit SoftHSM library (CHOOSE_x64). In your Visual Studio project properties, change the Platform target from 'Any CPU' or 'x86' to x64 to ensure compatibility.
Setting the Environment Variable
Another common cause is that the program cannot find the .conf file in SoftHSM. You must tell the library where to look by setting a system-wide environment variable. Create a new variable named SOFTHSM2_CONF and set its value to the full path of your configuration file (e.g., D:\SoftHSM2\etc\softhsm2.conf). Remember to restart Visual Studio after making the changes.
Additionally, you can verify whether the variable is found by adding this line:
Console.WriteLine($"Verifying variable: {Environment.GetEnvironmentVariable("SOFTHSM2_CONF")}");Console.WriteLine($"Verifying variable: {Environment.GetEnvironmentVariable(""SOFTHSM2_CONF"")}")If the console output returns blank, the program cannot find the environment variable. You must set it, restart Visual Studio or your computer, and try again.
What Causes a TimestampException?
TimestampException is thrown when a timestamp cannot be obtained or embedded during signing. It is a distinct type so that timestamp failures can be caught separately from general signing errors, and it reports a clear message rather than a low-level buffer error. Three situations produce it:
- The authority could not be reached. Check the endpoint URL and any outbound firewall or proxy rules on the signing host.
- The authority returned an empty token. The request was accepted but nothing usable came back. Try a different TSA.
- The token did not fit the reserved signature space. Some authorities return very large certificate chains. The exception message states the actual size against the reserved size.
try
{
hsmSigner.TimeStampUrl = "https://freetsa.org/tsr";
pdf.SignAndSave("signed.pdf", hsmSigner);
}
catch (TimestampException ex)
{
// TSA unreachable, empty token, or token too large for the reserved space
Console.WriteLine(ex.Message);
}
Because the exception is raised while the signature is being built, the output file is not written when it occurs.
Recommended Practices for Production HSM Deployment
When deploying HSM-signed PDFs in production environments, consider these additional security measures:
- Audit Logging: Implement comprehensive logging for all HSM operations to maintain compliance and track access
- Certificate Management: Regularly update and rotate certificates according to your organization's security policies
- Backup Procedures: Establish proper backup and recovery procedures for HSM configurations
- Performance Optimization: Monitor signing performance and implement caching strategies for frequently accessed certificates
These practices complement the standard PDF signing workflow and ensure your document security infrastructure remains reliable and compliant with industry standards.
Frequently Asked Questions
What is the main benefit of using an HSM to sign PDFs in C#?
Using an HSM (Hardware Security Module) to sign PDFs provides bank-level security because the private keys never leave the physical device, ensuring tamper-proof digital signatures. IronPDF enables this secure method using the PKCS#11 API.
How do I get started with signing a PDF using an HSM in C#?
To start signing a PDF with an HSM in C#, first install IronPDF via NuGet, configure your HSM device (or use SoftHSM for simulation), and then use the `UsbPkcs11HsmSigner` object with your credentials to sign and save the PDF.
What is the significance of the PKCS#11 API in PDF signing?
The PKCS#11 API is a standardized interface that allows IronPDF to interact securely with HSMs for digital signing operations, ensuring that private keys remain protected within the hardware device throughout the process.
Why would I use SoftHSM in PDF signing demonstration?
SoftHSM is used in PDF signing demonstrations to simulate a Hardware Security Module, allowing you to test and develop securely without needing a physical HSM device.
How can I verify my PDF after signing it with IronPDF and HSM?
After signing the PDF with IronPDF using an HSM, you can verify the signature by opening the signed document in a PDF viewer. This step ensures the integrity and authenticity of the signature.
How can I verify that my PDF was successfully signed with HSM?
After signing a PDF with IronPDF's HSM functionality, you can verify the signature by opening the signed PDF in any standard PDF viewer. The viewer will display the digital signature information and confirm the document's authenticity and integrity.
How do I add a trusted timestamp to an HSM-signed PDF?
Set the TimeStampUrl property on the signer to the endpoint of an RFC 3161 Time Stamp Authority before calling SignAndSave. The endpoint is supplied as a string, and the resulting signature carries a timestamp token that readers such as Adobe Acrobat recognize. Leaving the property null or empty skips timestamping entirely and makes no network call.
Which hash algorithm does the HSM timestamp use?
TimestampHashAlgorithm selects the digest requested from the timestamp authority and accepts SHA1, SHA256, or SHA512. The built-in UsbPkcs11HsmSigner defaults to SHA256 through its AHsmSigner base class. A custom signer that implements IHsmSigner directly without deriving from AHsmSigner receives the uninitialized enum value, which is SHA1, so it should set the property explicitly. Note that this is separate from the digestAlgorithm constructor parameter, which governs the signature itself rather than the timestamp.
What causes a TimestampException during HSM signing?
TimestampException is thrown when a timestamp cannot be obtained or embedded. The three causes are an unreachable Time Stamp Authority, an authority that returns an empty token, and a timestamped signature that does not fit the reserved signature space, which can happen when a TSA returns a very large certificate chain. The exception message states the actual size against the reserved size.
Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.