How to sign PDFs with USB tokens and HSM devices in C# and VB.NET
Trusted certificate authorities no longer allow exporting private keys for document signing certificates. Instead, private keys must be stored in secure hardware such as USB tokens, smart cards, or cloud-based HSMs.
You can use Docotic.Pdf library to sign PDF documents in .NET using HSM devices. This article describes integrations with Azure Key Vault, AWS KMS, and PKCS#11-compliant hardware.
9.7.18373 15,244 passed Total NuGet downloads 5,976,723You can get the library and a free time-limited license key on the Download C# .NET PDF library page.
Prerequisites
The code samples in this article use self-signed certificates to sign PDF documents. By default, PDF viewers do not trust these signatures. Self-signed certificates should be used only for testing, not in production environments. To generate trusted PDF signatures, use document signing certificates issued by third-party Certificate Authorities.
Get the right certificate
Prefer certificates from companies listed in the Adobe Approved Trust List (AATL). Adobe Acrobat trusts certificates from such companies by default and won't report the "Certificate validity is unknown" warning.
To create EU (eIDAS)-compliant documents, you need to use Qualified Certificates issued by providers listed in the European Union Trusted Lists (EUTL).
Learn how to use your certificate in .NET
When you order a document signing certificate, you will typically receive access to an HSM device. This could be a physical USB token or a cloud-based HSM solution.
Then you should learn how to sign data with this HSM. This is usually the most tricky part because all crypto providers are different. You should follow the documentation for your USB token or cloud API.
Ideally, you need to find a corresponding .NET SDK. Or at least find a native SDK and use it via interop in .NET.
Your goal is to get C# or VB.NET code that can sign any bytes using your certificate.
PDF API for external signing
Once you can sign any bytes with your HSM device, you are ready to sign PDF documents with Docotic.Pdf. To do so, implement the IPdfSigner interface and use it for signing.
Implement IPdfSigner interface
IPdfSigner
declares two members. The SignatureAlgorithm
property returns the algorithm used for
signing. Typically, you would use RSA or ECDSA algorithms.
The Sign
method is the key part of the external signing flow. Docotic.Pdf provides the digest of
a PDF document's bytes to this method. You should sign this digest with your HSM device and return
bytes of a digital signature. Note that you must not return a PKCS#7 object here. Just generate and
return the cryptographic signature.
Docotic.Pdf calls the Sign
method twice per each signature. The first call is to calculate the
space required for the signature. The second call is to actually sign the document.
Create PdfSigningOptions for external signing
Use PdfSigningOptions(IPdfSigner, X509Certificate2[]) constructor
to sign PDF documents with your IPdfSigner
implementation. Obtain a certificate chain
and provide it as the second argument. At least one certificate is required and the signing
certificate should come first.
The last piece of the puzzle is the PdfSigningOptions.DigestAlgorithm
property. The value of this
property must align with IPdfSigner.SignatureAlgorithm
and with the IPdfSigner.Sign
implementation.
Let's review how to sign PDFs with external signatures in common scenarios.
Sign PDF documents using PKCS#11 driver in C#
If your cryptographic USB token or smart card comes with a PKCS#11 driver, then you can use
Pkcs11Interop.X509Store or
Pkcs11Interop libraries in .NET. Pkcs11Interop
is compatible with Atos CardOS smart cards, YubiKey PIV, SmartCard-HSM, SafeNet ProtectServer HSM,
and other HSMs.
The Sign PDF documents using PKCS#11 drivers code sample shows how to sign PDF documents with a USB token or a smart card. You will need to customize these lines:
string pkcsLibraryPath = @"C:\Program Files\SoftHSM2\lib\softhsm2-x64.dll";
ulong slotId = 1743347971;
string alias = "YOUR-ALIAS";
string certLabel = "YOUR-CERTIFICATE";
string pin = "YOUR-PIN";
var digestAlgorithm = PdfDigestAlgorithm.Sha512;
Our sample configuration uses SoftHSM2 as an emulator of a USB token. In real applications, you do not need SoftHSM2. Instead, use the driver that comes with the USB token.
Sign PDF documents using Azure Key Vault
Microsoft Azure Key Vault securely stores certificates and keys. You will need to make the following preparations:
- Create an Azure account.
- Create an Azure user.
- Create a Key Vault.
- Allow access to the Key Vault for the selected Azure user.
- Add a document signing certificate to the Key Vault. You can import certificates from Integrated Certificate Authorities.
Then, use the Azure.Security.KeyVault.Keys
NuGet package for signing in .NET. Sample C#
code for signing with a key from Azure Key Vault:
var vaultUrl = new Uri("https://YOUR-VAULT.vault.azure.net/");
var credential = new DefaultAzureCredential();
var keyClient = new KeyClient(vaultUrl, credential);
KeyVaultKey key = keyClient.GetKey("YOUR-KEY");
var cryptoClient = new CryptographyClient(key.Id, credentials);
byte[] messageToSign = ..;
byte[] signature = cryptoClient.SignData(SignatureAlgorithm.RS512, messageToSign).Signature;
A related IPdfSigner
implementation might look like this:
class AzureSigner : IPdfSigner
{
private readonly CryptographyClient m_client;
private readonly AzureSignatureAlgorithm m_signingAlgorithm;
public AzureSigner(CryptographyClient client, AzureSignatureAlgorithm signingAlgorithm)
{
m_client = client;
m_signingAlgorithm = signingAlgorithm;
}
public PdfSignatureAlgorithm SignatureAlgorithm
{
get
{
if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256 ||
m_signingAlgorithm == AzureSignatureAlgorithm.RS384 ||
m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
return PdfSignatureAlgorithm.Rsa;
throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
}
}
public PdfDigestAlgorithm DigestAlgorithm
{
get
{
if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256)
return PdfDigestAlgorithm.Sha256;
if (m_signingAlgorithm == AzureSignatureAlgorithm.RS384)
return PdfDigestAlgorithm.Sha384;
if (m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
return PdfDigestAlgorithm.Sha512;
throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
}
}
public byte[] Sign(byte[] message)
{
return m_client.SignData(m_signingAlgorithm, message).Signature;
}
}
Finally, use this AzureSigner
class to sign PDF documents:
CryptographyClient cryptoClient = ..;
using X509Certificate2 cert = ..
var signer = new AzureSigner(cryptoClient, SignatureAlgorithm.RS512);
using var pdf = new PdfDocument();
PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);
var options = new PdfSigningOptions(signer, new[] { cert })
{
DigestAlgorithm = signer.DigestAlgorithm,
Field = field,
};
pdf.SignAndSave(options, ..);
Explore the full Sign PDF documents using Azure Key Vault code sample on GitHub.
Sign PDF documents using AWS KMS
AWS Key Management Service (KMS) is another secure storage for cryptographic keys. Preparation follows the same steps as Azure Key Vault:
- Create an AWS account.
- Create an IAM user.
- Add a signing key.
- Allow access to keys for the selected IAM user.
For AWS, you will need to use the AWSSDK.KeyManagementService
NuGet package in .NET. Sample code
for PDF signing is pretty close to the Azure scenario:
using var pdf = new PdfDocument();
PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);
var signingAlgorithm = SigningAlgorithmSpec.RSASSA_PSS_SHA_512;
var signer = new AwsSigner("arn:aws:kms:YOUR-id", signingAlgorithm)
var options = new PdfSigningOptions(signer, new[] { cert })
{
DigestAlgorithm = signer.DigestAlgorithm,
Field = field,
};
pdf.SignAndSave(options, ..);
class AwsSigner : IPdfSigner
{
private readonly string m_keyId;
private readonly SigningAlgorithmSpec m_signingAlgorithm;
public AwsSigner(string keyId, SigningAlgorithmSpec signingAlgorithm)
{
m_keyId = keyId;
m_signingAlgorithm = signingAlgorithm;
}
public PdfSignatureAlgorithm SignatureAlgorithm
{
get
{
if (m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256 ||
m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_384 ||
m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_512)
return PdfSignatureAlgorithm.Rsa;
throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
}
}
public PdfDigestAlgorithm DigestAlgorithm
{
get
{
string alg = m_signingAlgorithm.Value;
if (alg.EndsWith("256"))
return PdfDigestAlgorithm.Sha256;
if (alg.EndsWith("384"))
return PdfDigestAlgorithm.Sha384;
if (alg.EndsWith("512"))
return PdfDigestAlgorithm.Sha512;
throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
}
}
public byte[] Sign(byte[] message)
{
using var kmsClient = new AmazonKeyManagementServiceClient();
using var stream = new MemoryStream(message);
var signRequest = new SignRequest()
{
SigningAlgorithm = m_signingAlgorithm,
KeyId = m_keyId,
MessageType = MessageType.RAW,
Message = stream,
};
SignResponse signResponse = kmsClient.SignAsync(signRequest).GetAwaiter().GetResult();
return signResponse.Signature.ToArray();
}
}
The complete Sign PDF documents using AWS KMS code sample is also available on GitHub.
Conclusion
Leverage Docotic.Pdf library to sign PDF documents using USB tokens, smart cards, or
cloud-based HSMs. To achieve this, implement the IPdfSigner
interface in C# or VB.NET code.
Download and try code samples for digital signatures in PDF from GitHub. Check related articles for additional information:
Frequently Asked Questions
How do I sign a PDF using a USB token or smart card?
Use Docotic.Pdf and Pkcs11Interop .NET libraries to sign PDFs with either token or smart card. Refer to the Sign PDF documents using PKCS#11 driver in C# section for more details.
How do I use SoftHSM with my USB token or smart card?
SoftHSM simulates a real HSM device. You do not need SoftHSM if you have a USB token or a smart card. Instead, use the PKCS#11 driver provided with your HSM device.
How do I sign a PDF document with an external signature?
Check the Sign PDF documents using Azure Key Vault and Sign PDF documents using AWS KMS sections. External PDF signing with other cloud-based HSMs follows a similar process.
How do I use Azure Key Vault in .NET?
Use the Azure.Security.KeyVault.Keys
NuGet package. Try the Sign PDF documents using Azure Key Vault code
sample from GitHub.