Reputation: 311
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
Reputation: 21
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.
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
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
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
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
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