An Azure service that is used to manage and protect cryptographic keys and other secrets used by cloud apps and services.
Hi,
Thanks for your reply. I did look through the documentation. My challenge was figuring out how to sign a PDF with a certificate marked as non-exportable which does not allow the private key to leave Azure Key Vault not allowing me to create a .PFX to sign with. I ended up finding a PDF library called ABCPdf that supported using the KeyVaultClient.SignWithHttpMessagesAsync() method with is what I used to get this to work. I am posting my code below if anyone else is interested in this.
using WebSupergoo.ABCpdf12;
using WebSupergoo.ABCpdf12.Objects;
public void SignDoc()
{
var sourcePath = @"C:\TestDoc.pdf";
var signedPath = @"C:\TestDoc_Signed.pdf";
var cert = AzureHelper.GetX509();
using (Doc doc = new Doc())
{
doc.Read(sourcePath);
doc.Form.AddSignature(new XRect(), "Signature1");
Signature sig = (Signature)doc.Form.Fields("Signature1");
sig.Rect = XRect.FromLtwh(50, 50, 50, 10);
sig.Signer = "Signer Name";
sig.Value = "Signer Name";
sig.TimestampServiceUrl = new Uri("http://timestamp.digicert.com");
sig.CustomSigner = ExternalSigner;
sig.Sign(cert, true, new Oid(CryptoConfig.MapNameToOID("SHA512")), X509IncludeOption.EndCertOnly);
doc.Save(signedPath);
}
}
private byte[] ExternalSigner(byte[] data)
{
return AzureHelper.SignPdf(data);
}
//Create X509 from secret of certificate in Azure Key Vault
Public Shared Function GetX509() As X509Certificate2
Dim vaultUri As String = ConfigurationManager.AppSettings("AzureVaultUri")
Dim client = New SecretClient(New Uri(vaultUri), New DefaultAzureCredential(True))
Dim secret = client.GetSecret("testsign").Value.Value
Dim store As X509Store = New X509Store
Dim data As Byte() = Convert.FromBase64String(secret)
Dim cert As New X509Certificate2(data)
Return cert
End Function
//Sign PDF digest using Azure KeyVaultClient.SignWithHttpMessagesAsync()
public static byte[] SignPdf(byte[] data)
{
var vaultRootUri = ConfigurationManager.AppSettings("AzureVaultUri");
var keyName = ConfigurationManager.AppSettings("AzureKeyName");
var keyVersion = ConfigurationManager.AppSettings("AzureKeyVersion");
var hash = (new SHA512CryptoServiceProvider()).ComputeHash(data);
var keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync), new HttpClient());
return keyVault.SignWithHttpMessagesAsync(vaultRootUri, keyName, keyVersion, JsonWebKeySignatureAlgorithm.RS512, hash).Result.Body.Result;
}
/// <summary>
/// ''' Gets an access token for the keyvaultclient to use to access the Azure Key Vault. Uses the DefaultAzureCredential Object
/// ''' </summary>
/// ''' <param name="authority"></param>
/// ''' <param name="resource"></param>
/// ''' <param name="scope"></param>
/// ''' <returns></returns>
private static async Task<string> GetAccessTokenAsync(string authority, string resource, string scope)
{
var tokenCredential = new DefaultAzureCredential();
var accesstoken = await tokenCredential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes: new string[] { "https://vault.azure.net/.default" }));
return accesstoken.Token;
}
For the authentication I am using the DefaultAzureCredential to access the secret to build a X509 certificate, and I use it again to retrieve a token to be used to sign the pdf digest.