Retrieving an X509 certificate from the Azure Key Vault

About this page

This article describes how to retrieve an X509 certificate, including a private key, from the Azure Key Vault.

In this article, we use the 1E PowerShell Toolkit, but the code should be generally applicable to any environment. The technique discussed below does not rely on specific Microsoft-provided DLLs but uses oAuth and REST calls directly. This means that it should be portable to other programming environments easily, as long as there is some mechanism to create the X509 certificate from the keypair, as shown below.

Although not well documented, Azure Functions may not be able to obtain an oAuth token by leveraging a managed identity and may need to use a shared secret. Refer to Accessing the Azure Key Vault using REST and oAuth. However, regular code running in a VM will not have that problem.

The actual REST calls to retrieve certificate information from the Key Vault are entirely unaffected by how you obtained the bearer token in the first place, provided it has the appropriate privileges to access the data.

Retrieving certificates

There is a good deal of code floating around which creates certificates from PFX or PEM files directly, but this practice is very undesirable because things like private key passwords end up being hard-coded into the code, and in the case of PEM files, there’s no protection at all for the private key.

The Azure Key Vault allows processes associated with a managed identity to securely retrieve certificates. For processes located outside the SaaS boundary, it’s possible to use shared secrets. This isn’t ideal since these are equivalent in a sense to a private key password, but in many cases, it is possible to securely store that secret in a local credential store, and the administrator of the key vault can manage secret lifetime and rotation, which is not possible with private key passwords on PFX files scattered around the place.

The PowerShell function below shows how to hydrate an X509 certificate object directly from the key vault.

Copy
function AzureGetCertWithPrivateKey($bearerToken, $keyVaultUri, $certName)
{
    $token = "bearer " + $bearerToken.access_token
    $url = $keyVaultUri + "/certificates/" + $certName + "?api-version=7.4"
    $c = GetHttpRequestExternal $url @{Authorization=$($token)}
    # the cert properties include the URL of the associated secret in which the cert private key is stored
    $secretUrl = $c.sid + "?api-version=7.4"
    $secret = GetHttpRequestExternal $secretUrl @{Authorization=$($token)}
    $privateKeyBytes = [System.Convert]::FromBase64String($secret.value)
    $cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2(,$privateKeyBytes)
    return $cert
}

The bearer token could have been obtained directly from a managed identity, or via an oAuth grant flow that used a shared secret. Refer to Accessing the Azure Key Vault using REST and oAuth.

The certificate itself is directly retrieved in the first REST call. Note that GetHttpRequestExternal is just a thin wrapper over invoke-webrequest so it is not shown here. We supply the bearer token and request the certificate.

Its private key is not stored in the main certificate object. Instead, a matching secret, whose URL is given in the sid property of the certificate, is queried via a second REST call to get the private key which is contained in $secret.value. We then simply take this and create a new X509 certificate from the keypair in $privateKeyBytes.