19

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.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Nandun
  • 1,802
  • 2
  • 20
  • 35
  • 1
    @Adriano thank you. The 2nd half of your response helped resolve my issue. the Key point is how the AKV Certificate has 3 components - Key, Secret, & Certificate, and your break down of what exactly is stored in each of those. I'm posting the change I made to my code for others' reference. – Nandun Jul 23 '18 at 15:23
  • Did you ever find a proper solution? – Davy Jan 24 '20 at 14:55
  • @Davy the posted answer is the approach i went with. – Nandun Jan 24 '20 at 21:19
  • 2
    While suggested duplicate https://stackoverflow.com/questions/43837362/keyvault-generated-certificate-with-exportable-private-key has good details on why it does not work it really does not show C# code to do so. It looks like this one actually talks about C# version - so re-tagged and voted to re-open. – Alexei Levenkov Jun 05 '20 at 05:05

2 Answers2

34

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.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Nandun
  • 1,802
  • 2
  • 20
  • 35
  • 5
    Thank you very much for posting this answer here. The answer you refer contains only Power Shell code while this is in clean C#. Microsoft must really clarify in the documentation that GetCertificateAsync() method does not retrieve a useful certificate from vault. – Michael Chudinov Mar 08 '19 at 12:27
  • I voted to re-open this question since the "duplicate" refers to PowerShell and this question is about C#. However since I can't post a new answer, I provided a fuller working method to retrieve all certs w/ private keys [here](https://stackoverflow.com/a/65093184/393931). – Tobias J Dec 01 '20 at 15:14
  • You might want to mention this article which contains tips for how to solve the exceptions you might get when you run the above code: https://dotnetcoretutorials.com/2020/11/19/loading-certificates-from-azure-key-vault-in-net-and-getting-it-working-in-azure-app-service/ – Claus Appel Oct 29 '21 at 13:41
  • This method thows error when retrieving private key. "PrivateKey = 'x509Certificate.PrivateKey' threw an exception of type 'System.Security.Cryptography.CryptographicException'" – Kurkula Oct 12 '22 at 20:21
9

The latest version of the SDK (Azure.Security.KeyVault.Certificates 4.2.0) now has the DownloadCertificateAsync method, which obtains the full cert (i.e. private key too) in a straightforward way.

The documentation states:

Because Cer contains only the public key, this method attempts to download the managed secret that contains the full certificate.

X509Certificate2 cert = await certificateClient.DownloadCertificateAsync(certName);

jasper
  • 3,424
  • 1
  • 25
  • 46
  • 2
    What RBAC permissions does the managed identity need in order to be allowed to call that method? I can get it to work with "Key Vault Certificates Officer", but I am a bit sad to give my managed identity write-access to certificates. Do you know if I can get this to work with any level of read-only access? ("Reader" is not enough.) – Claus Appel Oct 29 '21 at 14:00
  • this method throws asymmetric exception while retrieving private key. – Kurkula Oct 12 '22 at 20:11
  • Work perfect for me - version 4.5.0, thanks ! – Dany Mar 20 '23 at 10:23