Azure Key Vault Certificates does not have the Private Key when retrieved via IKeyVaultClient.GetCertificateAsync

Nandun picture Nandun · Jul 21, 2018 · Viewed 9.5k times · Source

I have 2 approaches to do the same thing, but Azure has deprecated the one that works, and the other method doesn't work.

The approach that works, but is deprecated:

I store my PFX in Azure Key Vault Secrets. (when I create the secret I see a warning stating that this feature is deprecated)

and use the following code to retrieve it to create my certificate:

        SecretBundle secret = await keyVaultClient.GetSecretAsync(keyVaultUrl, "MyCert-Secret");
        X509Certificate2Collection exportedCertCollection = new X509Certificate2Collection();
        exportedCertCollection.Import(Convert.FromBase64String(secret.Value));
        X509Certificate2 certFromSecret = exportedCertCollection.Cast<X509Certificate2>().Single(s => s.HasPrivateKey);

credits to this answer

I'm able to use this certificate to host and access my application successfully.

The approach that doesn't work, but I should be using:

I store my certificate in the Azure Key vault Certificates

and use the following code to retrieve it and create the X509Certificate2:

        CertificateBundle certificateBundle = await keyVaultClient.GetCertificateAsync(keyVaultUrl, "MyCert-Certificate");
        X509Certificate2 certFromCertificate = new X509Certificate2(certificateBundle.Cer);

The problem with this approach is that the certificate does not contain the private key. i.e. certFromCertificate.HasPrivateKey is false.

My Questions

Why does certFromSecret have the PrivateKey, while certFromCertificate doesn't?

How can I retrieve a certificate from the key vault, where I can create a X509Certificate2 object to host my application in Kestrel with UseHttps.

Answer

Nandun picture Nandun · Jul 23, 2018

The 2nd part of @Adrian's answer explains the concepts around the Azure KV Certificates very well, and I have changed my code as below to get the full certificate including the private keys:

        SecretBundle secret = await kv.GetSecretAsync(keyVaultUrl, certName);
        X509Certificate2 certificate = 
                 new X509Certificate2(Convert.FromBase64String(secret.Value));

The trick was to use GetSecretAsync instead of GetCertificateAsync. Please refer to Adrian's SO answer to see why the secret had to be used to get the full certificate with Private keys.

Note that you should use "Certificate identifier" property (url with "/secrets/") from Azure certificate's property page.