I have a bunch of strings and pfx certificates, which I want to store in Azure Key vault, where only allowed users/apps will be able to get them. It is not hard to do store a string as a Secret, but how can I serialize a certificate in such way that I could retrieve it and deserialize as an X509Certificate2 object in C#?
I tried to store it as a key. Here is the Azure powershell code
$securepfxpwd = ConvertTo-SecureString -String 'superSecurePassword' -AsPlainText -Force
$key = Add-AzureKeyVaultKey -VaultName 'UltraVault' -Name 'MyCertificate' -KeyFilePath 'D:\Certificates\BlaBla.pfx' -KeyFilePassword $securepfxpwd
But when I tried to get it with GetKeyAsync method, I couldn't use it.
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 KeyVaultClient.GetSecretAsync($"{baseUri}{secretName}").ConfigureAwait(false);
string pfx = secretBundle.Value;
var bytes = Convert.FromBase64String(pfx);
var coll = new X509Certificate2Collection();
coll.Import(bytes, null, X509KeyStorageFlags.Exportable);
return coll[0];
}
Here is the script for uploading pfx certificate in python using azure cli
azure keyvault secret set --vault-name <Valut name> --secret-name <Secret Name> --value <Content of PFX file>
Getting the content of PFX file in python
fh = open(self.getPfxFilePath(), 'rb')
try:
ba = bytearray(fh.read())
cert_base64_str = base64.b64encode(ba)
password = self.getPassword()
json_blob = {
'data': cert_base64_str,
'dataType': 'pfx',
'password': password
}
blob_data= json.dumps(json_blob)
content_bytes= bytearray(blob_data)
content = base64.b64encode(content_bytes)
return content
finally:
fh.close
fh.close()