How to serialize and deserialize a PFX certificate in Azure Key Vault?
Here's a PowerShell script for you. Replace the file path, password, vault name, secret name.
$pfxFilePath = 'C:\mycert.pfx'
$pwd = '123'
$flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$collection.Import($pfxFilePath, $pwd, $flag)
$pkcs12ContentType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12
$clearBytes = $collection.Export($pkcs12ContentType)
$fileContentEncoded = [System.Convert]::ToBase64String($clearBytes)
$secret = ConvertTo-SecureString -String $fileContentEncoded -AsPlainText –Force
$secretContentType = 'application/x-pkcs12'
Set-AzureKeyVaultSecret -VaultName 'myVaultName' -Name 'mySecretName' -SecretValue $Secret -ContentType $secretContentType
This is a common question, so we are going to polish this up and release as a helper.
The script above strips the password because there's no value in having a password protected PFX and then storing the password next to it.
The original question asked how to retrieve the stored PFX as an X509Certificate2
object. Using a Base64 process similar to that posted by Sumedh Barde above (which has the advantage of stripping the password), the following code will return a X509 object. In a real application, the KeyVaultClient
should be cached if you're retrieving multiple secrets, and the individual secrets should also be cached.
public static async Task<X509Certificate2> GetSecretCertificateAsync(string secretName)
{
string baseUri = @"https://xxxxxxxx.vault.azure.net/secrets/";
var provider = new AzureServiceTokenProvider();
var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback));
var secretBundle = await client.GetSecretAsync($"{baseUri}{secretName}").ConfigureAwait(false);
string pfx = secretBundle.Value;
var bytes = Convert.FromBase64String(pfx);
var coll = new X509Certificate2Collection();
coll.Import(bytes, "certificatePassword", X509KeyStorageFlags.Exportable);
return coll[0];
}
Here is how I solved this. First, convert your PFX file to a base64 string. You can do that with these two simple PowerShell commands:
$fileContentBytes = get-content 'certificate.pfx' -Encoding Byte
[System.Convert]::ToBase64String($fileContentBytes) | Out-File 'certificate_base64.txt'
Create a secret in Azure Key Vault via the Azure Portal. Copy the certificate base64 string that you created previously and paste it in the secret value field in your Azure Key Vault via the Azure Portal. Then simply call the Azure Key Vault from the code to get the base64 string value and convert that to a X509Certificate2
:
private async Task<X509Certificate2> GetCertificateAsync()
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("https://path/to/key/vault").ConfigureAwait(false);
var pfxBase64 = secret.Value;
var bytes = Convert.FromBase64String(pfxBase64);
var coll = new X509Certificate2Collection();
coll.Import(bytes, "certificatePassword", X509KeyStorageFlags.Exportable);
return coll[0];
}