How to: Use Azure Key Vault API to Sign a PDF Document
- 7 minutes to read
This example demonstrates how to use the Azure Key Vault API to sign a PDF document.
Prerequisites
- An Azure subscription.
- An existing Azure Key Vault. If you need to create an Azure Key Vault, you can use the Azure Portal or Azure CLI.
Obtain a Certificate
Use Azure Key Vault API to obtain a certificate or the whole certificate chain.
The AzureKeyVaultClient
class below uses the Azure Key Vault API to retrieve a certificate or a certificate chain, and signs the document hash with a private key stored on Azure.
public class AzureKeyVaultClient
{
public static AzureKeyVaultClient CreateClient(string keyVaultUrl)
{
return new AzureKeyVaultClient(new KeyClient(new Uri(keyVaultUrl), new DefaultAzureCredential()));
}
readonly KeyClient client;
DefaultAzureCredential defaultAzureCredential;
AzureKeyVaultClient(KeyClient client)
{
this.client = client;
var credentialOptions = new DefaultAzureCredentialOptions
{
ExcludeInteractiveBrowserCredential = false,
ExcludeVisualStudioCodeCredential = true
};
defaultAzureCredential = new DefaultAzureCredential(credentialOptions);
}
public byte[] Sign(string keyId, SignatureAlgorithm algorithm, byte[] digest)
{
KeyVaultKey cloudRsaKey = client.GetKey(keyId);
var rsaCryptoClient = new CryptographyClient(cloudRsaKey.Id, defaultAzureCredential);
SignResult rsaSignResult = rsaCryptoClient.Sign(algorithm, digest);
Debug.WriteLine($"Signed digest using the algorithm {rsaSignResult.Algorithm}, with key {rsaSignResult.KeyId}. " +
$"The resulting signature is {Convert.ToBase64String(rsaSignResult.Signature)}");
return rsaSignResult.Signature;
}
public byte[] GetCertificateData(string keyVaultUrl, string certificateIdentifier)
{
var certificateClient = new CertificateClient(new Uri(keyVaultUrl), defaultAzureCredential);
KeyVaultCertificateWithPolicy cert = certificateClient.GetCertificate(certificateIdentifier);
return cert.Cer;
}
}
Create a Pkcs7SignerBase descendant to create a custom signer class. This class helps you to retrieve a certificate or a certificate chain, and sign the document hash. To calculate the document hash, you can use the DigestCalculator class. This class supports the following hashing algorithms: SHA1, SHA256, SHA384, and SHA512. Override the Pkcs7SignerBase.DigestCalculator
property to return a DigestCalculator
instance.
The code sample below shows the AzureKeyVaultSigner
class that is the Pkcs7SignerBase descendant. The SignDigest
method overload calls the AzureKeyVaultClient.Sign
method to sign the calculated document hash with a private key (specified by the keyId
variable).
public class AzureKeyVaultSigner : Pkcs7SignerBase
{
// OID for RSA signature algorithm:
const string PKCS1RsaEncryption = "1.2.840.113549.1.1.1";
readonly AzureKeyVaultClient keyVaultClient;
readonly string keyId;
readonly byte[][] certificateChain;
// Must match with key algorithm (RSA or ECDSA)
// OID for RSA PKCS1RsaEncryption(1.2.840.113549.1.1.1) can have any digest algorithm
// For ECDSA, use OIDs from this family: http://oid-info.com/get/1.2.840.10045.4.3
// Specified digest algorithm must be the same as to DigestCalculator algorithm
protected override IDigestCalculator DigestCalculator => new DigestCalculator(HashAlgorithmType.SHA256); //Digest algorithm
protected override string SigningAlgorithmOID => PKCS1RsaEncryption;
protected override IEnumerable<byte[]> GetCertificates() => certificateChain;
public AzureKeyVaultSigner(AzureKeyVaultClient keyVaultClient, string certificateIdentifier, string keyId, string keyVaultUri, ITsaClient tsaClient = null,
IOcspClient ocspClient = null, ICrlClient crlClient = null,
PdfSignatureProfile profile = PdfSignatureProfile.Pdf) : base(tsaClient, ocspClient, crlClient, profile)
{
this.keyVaultClient = keyVaultClient;
this.keyId = keyId;
// Get certificate (without a public key) from Azure Key Vault storage
// or create a new one at runtime
// You can get the whole certificate chain here
certificateChain = new byte[][] { keyVaultClient.GetCertificateData(keyVaultUri, certificateIdentifier) };
}
protected override byte[] SignDigest(byte[] digest)
{
return keyVaultClient.Sign(keyId, SignatureAlgorithm.RS256, digest);
}
}
Sign the Document
Pass the Pkcs7SignerBase descendant object to the PdfSignatureBuilder object constructor to apply a signature to a new form field. Call the PdfDocumentSigner.SaveDocument(String, PdfSignatureBuilder[]) method and pass the PdfSignatureBuilder
object as the method parameter to sign the document and save the result.
using DevExpress.Office.DigitalSignatures;
using DevExpress.Office.Tsp;
using DevExpress.Pdf;
using System;
using System.Diagnostics;
// ...
using (var signer = new PdfDocumentSigner(@"Document.pdf"))
{
// Create a timestamp:
ITsaClient tsaClient = new TsaClient(new Uri(@"https://freetsa.org/tsr"), HashAlgorithmType.SHA256);
// Specify the signature's field name and location:
int pageNumber = 1;
var description = new PdfSignatureFieldInfo(pageNumber);
description.Name = "SignatureField";
description.SignatureBounds = new PdfRectangle(10, 10, 50, 150);
// Specify your Azure Key Vault URL - (vaultUri)
const string keyVaultUrl = "";
// Specify the Azure Key Vault Certificate ID (certId)
string certificateId = "";
// Specify the Azure Key Vault Key ID for a certificate
string keyId = "";
// Create a custom signer object:
var client = AzureKeyVaultClient.CreateClient(keyVaultUrl);
AzureKeyVaultSigner azureSigner = new AzureKeyVaultSigner(client, certificateId, keyId, keyVaultUrl, tsaClient);
// Apply a signature to a new form field:
var signatureBuilder = new PdfSignatureBuilder(azureSigner, description);
// Specify an image and signer information:
signatureBuilder.SetImageData(System.IO.File.ReadAllBytes("signature.jpg"));
signatureBuilder.Location = "LOCATION";
// Sign and save the document:
string output = "SignedDocument.pdf";
signer.SaveDocument(output, signatureBuilder);
Process.Start(new ProcessStartInfo(output) { UseShellExecute = true });
}