Get X509 Certificate WITH PRIVATE KEY from Azure Keyvault c#

Viewed 2257

I am currently working on an authentication server developed in C #, this one is hosted on an azure function app, and I use a KeyVault where my secrets are stored. My problem is the following, in my keyvault, I store a certificate (certificate + private key) and when I retrieve it in my code, but the private key is not returned. if I test the following method: HasPrivateKey the code returns false ... but if i use the same .pfx in localy the code return me true ... my code:

 var client = new CertificateClient(vaultUri: new Uri("https://diiage2p1g3chest.vault.azure.net/"),credential: new DefaultAzureCredential());
         KeyVaultCertificate kcertificate = client.GetCertificate("try");
         var cert_content = kcertificate.Cer;
       X509Certificate2  certificate = new X509Certificate2(cert_content, "password", X509KeyStorageFlags.EphemeralKeySet);

any idea where the problem comes from?

4 Answers

The simplest way to get the full bytes of a certificate with its private information from keyvault is this. Please note you need permission to get secrets in your client id.

You need the following packages:

Azure.Identity
Azure.Security.KeyVault.Secrets

The following are deprecated:

Microsoft.IdentityModel.Clients.ActiveDirectory
Microsoft.Azure.KeyVault

Code:

using System;
using System.Threading.Tasks;
using Azure;
using Azure.Identity;
using System.Security.Cryptography.X509Certificates;
using Azure.Security.KeyVault.Secrets;

...

public async Task<X509Certificate2> GetCertificate(string certificateName,string clientId, string clientSecret, string keyVaultAddress, string tenantId)
        {
            ClientSecretCredential clientCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
            var secretClient = new SecretClient(new Uri(keyVaultAddress), clientCredential);
            var response = await secretClient.GetSecretAsync(certificateName);
            var keyVaultSecret = response?.Value;
            if(keyVaultSecret != null)
            {
                var privateKeyBytes = Convert.FromBase64String(keyVaultSecret.Value);
                return new X509Certificate2(privateKeyBytes);
            }
            return null;
        }

CertificateClient has a method that returns a certificate with private key, but it's not obvious that's what it does.

From CertificateClient.DownloadCertificate:

Because Cer contains only the public key, this method attempts to download the managed secret that contains the full certificate. If you do not have permissions to get the secret, RequestFailedException will be thrown with an appropriate error response. If you want an X509Certificate2 with only the public key, instantiate it passing only the Cer property. This operation requires the certificates/get and secrets/get permissions.

So just refactor your code to use DownloadCertificate to get a cert with the private key.

var client = new CertificateClient(new Uri("https://diiage2p1g3chest.vault.azure.net/"),  new DefaultAzureCredential()); 
X509Certificate2 certificate = client.DownloadCertificate("try");

When you use GetCertificate, you only get the certificate with the public key and this is of course quite annoying and surprising. So, to get the private key part, you have to get it separately as a secret.

I answered the same question here and in my answer you also find some some code that you can used.

 var _keyVaultName = $"VAULTURL";
        var secretName = "CERTIFICATENAME";
        var azureServiceTokenProvider = new AzureServiceTokenProvider();
        var _client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
        var secret = _client.GetSecretAsync(_keyVaultName, secretName);
        var privateKeyBytes = Convert.FromBase64String(secret.Result.Value);
        certificate = new X509Certificate2(privateKeyBytes, string.Empty);

i solve my probleme like that :)

Related