0

RSA keypair is in Azure KeyVault Key. I can use keyClient to access the key and use CryptographyClient to encrypt/decrypt

https://azuresdkdocs.blob.core.windows.net/$web/cpp/azure-security-keyvault-keys/4.2.0/md_samples_sample4_encrypt_decrypt.html

Now I'd like to use the above same RSA keypair to encrypt/decrypt JWE as mentioned in this article https://www.scottbrady91.com/c-sharp/json-web-encryption-jwe-in-dotnet-core

keyClient can get JsonWebKey from Azure. But it can't get private key. How can I do the JWE decryption? Or is it the right approach?

Updated:

RSA private key can't be downloaded from Azure KeyVault Key. We can download public and private key for certificate. See accepted answer.

Abe
  • 310
  • 3
  • 15

1 Answers1

1

To get the private key part, you need to get it as a "secret"; otherwise, you only get the public key.

The code below is the one I use to get it from Azure Key Vault

    /// <summary>
    /// Load a certificate (with private key) from Azure Key Vault
    ///
    /// Getting a certificate with private key is a bit of a pain, but the code below solves it.
    /// 
    /// Get the private key for Key Vault certificate
    /// https://github.com/heaths/azsdk-sample-getcert
    /// 
    /// See also these GitHub issues: 
    /// https://github.com/Azure/azure-sdk-for-net/issues/12742
    /// https://github.com/Azure/azure-sdk-for-net/issues/12083
    /// </summary>
    /// <param name="config"></param>
    /// <param name="certificateName"></param>
    /// <returns></returns>
    public static X509Certificate2 LoadCertificate(IConfiguration config, string certificateName)
    {
        string vaultUrl = config["Vault:Url"] ?? "";
        string clientId = config["Vault:ClientId"] ?? "";
        string tenantId = config["Vault:TenantId"] ?? "";
        string secret = config["Vault:ClientSecret"] ?? "";

        Console.WriteLine($"Loading certificate '{certificateName}' from Azure Key Vault");

        var credentials = new ClientSecretCredential(tenantId: tenantId, clientId: clientId, clientSecret: secret);
        var certClient = new CertificateClient(new Uri(vaultUrl), credentials);
        var secretClient = new SecretClient(new Uri(vaultUrl), credentials);

        var cert = GetCertificateAsync(certClient, secretClient, certificateName);

        Console.WriteLine("Certificate loaded");
        return cert;
    }


    /// <summary>
    /// Helper method to get a certificate
    /// 
    /// Source https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs
    /// </summary>
    /// <param name="certificateClient"></param>
    /// <param name="secretClient"></param>
    /// <param name="certificateName"></param>
    /// <returns></returns>
    private static X509Certificate2 GetCertificateAsync(CertificateClient certificateClient,
                                                            SecretClient secretClient,
                                                            string certificateName)
    {

        KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);

        // Return a certificate with only the public key if the private key is not exportable.
        if (certificate.Policy?.Exportable != true)
        {
            return new X509Certificate2(certificate.Cer);
        }

        // Parse the secret ID and version to retrieve the private key.
        string[] segments = certificate.SecretId.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
        if (segments.Length != 3)
        {
            throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length}, URI: {certificate.SecretId}");
        }

        string secretName = segments[1];
        string secretVersion = segments[2];

        KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);

        // For PEM, you'll need to extract the base64-encoded message body.
        // .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
        if ("application/x-pkcs12".Equals(secret.Properties.ContentType, StringComparison.InvariantCultureIgnoreCase))
        {
            byte[] pfx = Convert.FromBase64String(secret.Value);
            return new X509Certificate2(pfx);
        }

        throw new NotSupportedException($"Only PKCS#12 is supported. Found Content-Type: {secret.Properties.ContentType}");
    }
}
Tore Nestenius
  • 16,431
  • 5
  • 30
  • 40
  • This is a good answer, but if you start with downloading the secret in your application you greatly reduce security. Check out https://svrooij.io/2022/06/01/certificate-extraction-encryption/ for more info – Stephan Feb 25 '23 at 22:09
  • Great blog post, but in when you deal with signing and validating JWT/JWE tokens at scale, you typically need the private key in the token issuer. The receiver of the tokens only needs the public key – Tore Nestenius Feb 26 '23 at 08:35