Damien PAYET
Damien PAYET

Reputation: 311

Get X509 Certificate WITH PRIVATE KEY from Azure Keyvault c#

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?

Upvotes: 11

Views: 16485

Answers (5)

CSMan
CSMan

Reputation: 21

Adding Certificate to KeyVault

  1. Head to KeyVault
  2. Choose Certificates from side-bar
  3. Click +Generate/Import
  4. Select Import
  5. Fill in details
  6. Click Create

Giving Access to Application

To retrieve the public certificate, the application can be added to the access policies, however, to retrieve the full certificate including the private key, a longer procedure is required.

  1. Open Microsoft Entra ID
  2. Select 'App registrations' from side bar
  3. If the application is not yet registered create one
    1. Click '+ New Registration'
    2. Fill details
    3. Click 'Register'
  4. Navigate to KeyVault
  5. Select 'Access policies' from sidebar
  6. Select Permissions Get and List Certificates
  7. As a Principal use the created app registration
  8. Create

Accessing Certificate from Code

  1. Get Vault URI from the vault overview page
  2. Get the certificate name from the vault certificates page
  3. Get application tenant ID, and client ID from App registration in Microsoft Entra ID
  4. Navigate to 'Certificates & Secrets' from the sidebar of App registration
  5. Create a new client secret and the value will be the client's secret
  6. Use the following code
    public X509Certificate2 GetCertificate(string vaultName, string certName, string tenantId, string clientId, string clientSecret)
    {
                ClientSecretCredential credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
                var client = new SecretClient(new Uri(vaultName), credentials);
                KeyVaultSecret secret = client.GetSecret(certName);
                byte[] certificate = Convert.FromBase64String(secret.Value);
                X509Certificate2 x509 = new X509Certificate2(certificate);
                return x509;
    }

Upvotes: 1

lucky
lucky

Reputation: 1

        X509Certificate2 certificate;
        ClientSecretCredential clientCredential = new ClientSecretCredential(TenantId,ClientId,ClientSecret);
        var secretClient = new SecretClient(new Uri(KeyVaultUrl), clientCredential);
        var response = await secretClient.GetSecretAsync(individualEnrollment.DeviceId.Replace("-", ""));
        var keyVaultSecret = response?.Value;
        var privateKeyBytes = Convert.FromBase64String(keyVaultSecret.Value);
        certificate = new X509Certificate2(privateKeyBytes);

        
        using (var security = new SecurityProviderX509Certificate(certificate))
        using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly))
        {
            ProvisioningDeviceClient provClient =
                ProvisioningDeviceClient.Create(GlobalDeviceEndpoint, Scope, security, transport);

            var deviceClient = await ProvisionX509Device(provClient, security);
            
        }

Upvotes: 0

Daniel Gimenez
Daniel Gimenez

Reputation: 20494

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");

Upvotes: 23

Ruben G.L
Ruben G.L

Reputation: 61

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;
        }

Upvotes: 6

Damien PAYET
Damien PAYET

Reputation: 311

 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 :)

Upvotes: 0

Related Questions