IRONSOFTWAREHOME

How to Digitally Sign PDFs with C# Using HSM

Curtis Chau
Curtis Chau
Updated: August 27, 2026

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#
  1. Install IronPDF via NuGet: Install-Package IronPdf

  2. Configure your HSM device (or use SoftHSM for testing)

  3. Create a UsbPkcs11HsmSigner with your HSM credentials:

    1. 1Install IronPDF with NuGet Package Manager

      PM > Install-Package IronPdf

    2. 2Copy and run this code snippet.

         var hsmSigner = new UsbPkcs11HsmSigner(libraryPath, pin, tokenLabel, keyLabel);
      C#
    3. 3Deploy to test on your live environment

      Start using IronPDF in your project today with a free trial
      arrow pointer
  4. Generate your PDF and sign it:

    var pdf = renderer.RenderHtmlAsPdf("<h1>Document</h1>");
    pdf.SignAndSave("signed.pdf", hsmSigner);
  5. 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 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);

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);

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);
C#

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.

Please note: This is a different setting from the 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.
Warning: That SHA-256 default comes from 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.

My favorite library of this kind is IronPDF. It allows for fast and efficient manipulation of PDF files. It also has many valuable features, like exporting to PDF/A format and digitally signing PDF documents.

Milan Jovanovic

Microsoft MVP

View case study

IronOCR means we can save $40,000 annually from manual processing, while enhancing productivity and freeing up resources for high-impact tasks. I would highly recommend it.

Brent Matzelle

Chief Technology Officer, OPYN

View case study

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.

David Jones

Lead Software Engineer, Agorus Build

View case study

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.

PKCS#11 HSM initialization error showing CKR_GENERAL_ERROR in console output with full stack trace

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.

Visual Studio build configuration showing Platform target set to x64 architecture with conditional compilation symbols

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.

Windows System Variables dialog with SOFTHSM2_CONF environment variable highlighted showing HSM configuration path

Additionally, you can verify whether the variable is found by adding this line:

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);
}
C#

Because the exception is raised while the signature is being built, the output file is not written when it occurs.

When deploying HSM-signed PDFs in production environments, consider these additional security measures:

  1. Audit Logging: Implement comprehensive logging for all HSM operations to maintain compliance and track access
  2. Certificate Management: Regularly update and rotate certificates according to your organization's security policies
  3. Backup Procedures: Establish proper backup and recovery procedures for HSM configurations
  4. 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
Technical Writer

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.

...
Read More

Ready to Get Started?

Nuget Downloads 21,105,021Version:2026.9just released

Get your free 30-day Trial Key instantly.
No credit card or account creation required
C# NuGet Library for PDF
Install with NuGet

Version: 2026.9

PM > Install-Package IronPdf
nuget.org/packages/IronPdf/
  1. In Solution Explorer, right-click References, Manage NuGet Packages
  2. Select Browse and search "IronPdf"
  3. Select the package and install
C# PDF DLL
Download DLL

Version: 2026.9

or download Windows Installer here.

  1. Download and unzip IronPDF to a location such as ~/Libs within your Solution directory
  2. In Visual Studio Solution Explorer, right click References. Select Browse, "IronPdf.dll"

Licenses from $999

Key in blue circle

Get your free 30-day Trial Key instantly.

Your trial license will be sent to your email address

No limitations. 100% unlocked. No credit card.

OR
bullet_checkedNo credit card or account creation requiredNo limitations. 100% unlocked. No credit card.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
Book your free Live Demo
Booking Badge

Trusted by Millions of Engineers Worldwide

Iron Software's customer logos
Get Your No-Obligation Consult
Complete the form below or email sales@ironsoftware.com
Your details will always be kept confidential.
Trusted by Millions of Engineers Worldwide
Iron Software's customer logos
Get your free 30-day Trial Key instantly.
No credit card or account creation required
C# NuGet Library for PDF
Install with NuGet

Version: 2026.9

PM > Install-Package IronPdf
nuget.org/packages/IronPdf/
  1. In Solution Explorer, right-click References, Manage NuGet Packages
  2. Select Browse and search "IronPdf"
  3. Select the package and install
C# PDF DLL
Download DLL

Version: 2026.9

or download Windows Installer here.

  1. Download and unzip IronPDF to a location such as ~/Libs within your Solution directory
  2. In Visual Studio Solution Explorer, right click References. Select Browse, "IronPdf.dll"

Licenses from $999