I am using Azure Key Vault to protect our keys and secrets, but I am unsure how I can use the KeyBundle I retrieve using the .net SDK. How can I create an X509Certificate2 object?
4 Answers
When you import / create a certificate in KeyVault, 3 entities are created:
Certificate - contains all the relevant details about the certificate, including its public part (i.e. public key, validity period, thumbprint etc.)
Secret - contains the private key (which is the private part of the certificate) in base64
Key - I don't know, but irrelevant for this thread.
You could create X509Certificate2
object with either the Certificate object or the Secret object.
In case you want the X509Certificate2
to contain the private key, then of course you would need to fetch the Secret entity's value and do the following:
SecretBundle certificatePrivateKeySecretBundle =
await keyVaultClient.GetSecretAsync(certificateIdentifierSecretPart);
byte[] privateKeyBytes = Convert.FromBase64String(certificatePrivateKeySecretBundle.Value);
X509Certificate2 certificateWithPrivateKey = new X509Certificate2(privateKeyBytes, (string) null, X509KeyStorageFlags.MachineKeySet);
The certificateIdentifierSecretPart
equals the certificate's secret part path:
https://<vault name>.vaults.azure.net/secrets/<certificate name>
Note the /secrets/ path.

- 12,981
- 8
- 59
- 68

- 5,342
- 6
- 42
- 70
-
1This worked for me: keyVaultClient.GetSecretAsync(
, – tobbenb3 Jul 26 '18 at 14:15) -
In .Net Core 2.1 I am `using Microsoft.Azure.KeyVault;` but `KeyVaultClient` does not contain definition for `GetSecretAsync`. Have Azure SDK installed / updated. :/ – ttugates Jul 31 '18 at 20:08
-
1The `certificateIdentifierSecretPart` can be determined from the KeyBundle: ```var kb = await keyVaultClient.GetCertificateAsync("https://
.vault.azure.net", " – Paul Schaeflein Jan 26 '19 at 01:53"); var certificateIdentifierSecretPart = kb.SecretIdentifier.Identifier; ``` Then get the SecretBundle as shown above. -
Great answer! Any idea where this is documented (specifically the `/secrets/` path)? – Ohad Schneider Jun 24 '19 at 12:57
-
I am using above code snippet in Azure web app, it works great, but this Saturday we received "Keyset does not exist" error. It happened just once, I am unable to diagnose the cause of that error, my hunch is that it has something to do with X509KeyStorageFlags.MachineKeySet. Is it safe to MachineKeySet in Azure web app with shared plan? – Sadiq Khoja Nov 19 '19 at 20:50
-
1The certificate represents the certificate just created, the Key represents the private part of the certificate, and the Secret has the certificate in PFX format (just as if you had uploaded a PFX as a Secret). Since the certificate created above is exportable, the Secret contains the Private portion of the key as well. To recreate the certificate locally in memory, we use the below code. Refer to: https://www.rahulpnath.com/blog/signing-a-pdf-file-using-azure-key-vault/ – Meng Li Jun 03 '20 at 05:28
November 2020 Update:
In the current version of Azure Key Vault, Certificates are a first class concept rather than a type of Secret.
If your Key Vault instance already has a certificate with an exportable private key, you'd fetch it and hydrate an X509Certificate2
as follows:
Create the required clients using a DefaultAzureCredential
var certClient = new CertificateClient(new Uri("https://yourKeyVault.vault.azure.net/"), new DefaultAzureCredential());
var secretClient = new SecretClient(new Uri("https://yourKeyVault.vault.azure.net/"), new DefaultAzureCredential());
Get the certificate, which includes a link to the private key secret.
Note: The latest (4.2.0 beta) version of the Key Vault Secrets library includes a helper class called KeyVaultSecretIdentifier that does this parsing for you.
Response<KeyVaultCertificateWithPolicy> certResponse = await certClient.GetCertificateAsync("testCert");
// If using client version 4.2.0 or later
KeyVaultSecretIdentifier identifier = new KeyVaultSecretIdentifier(certResponse.Value.SecretId);
// Else, Get the secretId and parse out the parts needed to fetch the secret.
Uri secretId = certResponse.Value.SecretId;
var segments = secretId.Segments;
string secretName = segments[2].Trim('/');
string version = segments[3].TrimEnd('/');
Get the secret for the certificate and use it to construct a new X509Certificate2
.
// If using client version 4.2.0 or later
Response<KeyVaultSecret> secretResponse = await secretClient.GetSecretAsync(identifier.Name, identifier.Version);
// else
Response<KeyVaultSecret> secretResponse = await secretClient.GetSecretAsync(secretName, version);
KeyVaultSecret secret = secretResponse.Value;
byte[] privateKeyBytes = Convert.FromBase64String(secret.Value);
var cert = new X509Certificate2(privateKeyBytes);
For more information about the latest Key Vault Certificate and Secret clients, see their respective README docs here:
Azure.Security.KeyVault.Certificates (migration guide from the old version)
Azure.Security.KeyVault.Secrets (migration guide from the old version)

- 205
- 1
- 8

- 2,676
- 2
- 26
- 26
-
1I wanted to add to this that the old Microsoft.Azure.KeyVault packages are deprecated. These new packages are faster, easier to customize, under active development (by Christopher and myself, no less), and more. Check out: https://aka.ms/azsdk/intro for more benefits of migrating. – Heath Nov 13 '20 at 18:14
-
Thanks for this information. I noticed that, in order to get a `X509Certificate2` object from the Key Vault certificates, the [Azure.Security.KeyVault.Secrets](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Secrets/README.md) client is enough because the name of the certificate can be used as a parameter of the `GetSecretAsync`. I also noticed the identity that will access the Key Vault must have the Secret Get access policy (and not the Certificate Get access policy). – Michaël Maillot Nov 16 '20 at 15:06
-
1@michaelmaillot You are correct about the secret name, however that is an implementation detail that is not guaranteed to be consistent. It may end up always being true that the secret name and the certificate names will match, but the extra call just ensures that secret name is always accurate. – Christopher Scott Nov 16 '20 at 16:52
-
You cannot use the KeyBundle result as an X509Certificate2 object because it simply represents the public key portion of a key pair here (no issuer). See the methods in KeyVaultClientExtensions for functions to encrypt data, verify signatures, etc. using this KeyBundle object.

- 588
- 1
- 4
- 9
-
Thanks for your comment, I misunderstood how how to use keys with key vault. I found a great explanation here for what I wanted to do (store a pfx in the key vault): http://stackoverflow.com/questions/33728213/how-to-store-pfx-certificate-in-azure-key-vault – Dan O'Leary May 16 '16 at 08:58
When using the new Azure.Security.KeyVault.* libraries and to build on what Christopher Scott has answered, you can load all active and non-expired versions and skip the GetCertificate and parsing step with the following:
public static IEnumerable<X509Certificate2> LoadCertificateVerisons(
string keyVaultName,
string certificateName)
{
var keyVaultUrl = new Uri($"https://{keyVaultName}.vault.azure.net");
var certificateClient = new CertificateClient(keyVaultUrl, new AzureCliCredential());
var secretClient = new SecretClient(keyVaultUrl, new AzureCliCredential());
var versions = certificateClient.GetPropertiesOfCertificateVersions(certificateName).ToArray();
foreach (var certificate in versions)
{
if (!certificate.Enabled.GetValueOrDefault(false) ||
certificate.ExpiresOn <= DateTimeOffset.UtcNow) continue;
var certificateSecret = secretClient.GetSecret(certificate.Name, certificate.Version).Value;
var privateKey = Convert.FromBase64String(certificateSecret.Value);
yield return new X509Certificate2(privateKey, (string) null, X509KeyStorageFlags.MachineKeySet);
}
}

- 8,338
- 14
- 57
- 95
-
Important note: the additional options in the certificate creation command were important. I always got "file not found" errors without the additional parameters in : `new X509Certificate2(privateKey, (string) null, X509KeyStorageFlags.MachineKeySet);` – Kerry Jan 29 '22 at 16:45