OdeToCode IC Logo

Decryption with Azure Key Vault

Thursday, March 8, 2018

The next few posts are tips for developers using Azure Key Vault.

The documentation and examples for Key Vault can be frustratingly superficial. The goal of the next few posts is to clear up some confusion I’ve seen. In this first post we’ll talk about encryption and decryption with Key Vault.

But first, we’ll set up some context.

Old Habits Are Hard to Break

Over the years we’ve learned to treat passwords and other secrets with care. We keep secrets out of our source code and encrypt any passwords in configuration files. These best practices add a layer of security that helps to avoid accidents. Given how entrenched these practices are, the following line of code might not raise any eyebrows.

var appId = Configuration["AppId"];
var appSecret = Configuration["AppSecret"];

var encyptedSecret = keyVault.GetSecret("dbcredentials", appId, appSecret");

var decryptionKey = Configuration["DecryptKey"];
var connectionString = CryptoUtils.Decrypt(encryptedSecret, decryptKey);

Here are three facts we can deduce from the above code.

1. The application’s configuration sources hold a secret key to access the vault.

2. The application needs to decrypt the connection strings it fetches from the vault.

3. The application’s configuration sources hold the decryption key for the connection string.

Let’s work backwards through the list of items to see what we can improve.

Encryption and Decryption with Key Vault

Most presentations about Key Vault will tell you that you can store keys and secrets in the vault. Keys and secrets are two distinct categories in Key Vault. A secret can be a connection string, a password, an access token, or nearly anything you can stringify. A key, however, can only be a specific type of key. Key Vault’s current implementation supports 2048-bit RSA keys. You can have soft keys, which Azure encrypts at rest, or create keys in a hardware security module (HSM). Soft keys and HSMs are the two pricing tiers for Key Vault.

You can use an RSA key in Key Vault to encrypt and decrypt data. There is a special advantage to using key vault for decryption which we’ll talk about in just a bit. However, someone new to the cryptosystem world needs to know that RSA keys, which are asymmetric keys and computationally expensive compared to symmetric keys, will only encrypt small amounts of data. So, while you won’t use an RSA key to decrypt a database connection string, you could use an RSA key to decrypt a symmetric key the system uses for crypto operations on a database connection string.

Working with the REST API Wrapper

The .NET wrapper for Azure Key Vault is in the Microsoft.Azure.KeyVault package. If you want to use the client from a system running outside of Azure, you’ll need to authenticate using the Microsoft.IdentityModel.Clients.ActiveDirectory package. I’ll show how to authenticate using a custom application ID and secret in this post, but if you are running a system inside of Azure you should use a system’s Managed Service Identity instead. We’ll look at MSI in a future post.

The Key Vault client has a few quirks and exposes operations at a low level. To make the client easier to work with we will create a wrapper.

public class KeyVaultCrypto : IKeyVaultCrypto
    private readonly KeyVaultClient client;
    private readonly string keyId;

    public KeyVaultCrypto(KeyVaultClient client, string keyId)
        this.client = client;
        this.keyId = keyId;

    public async Task<string> DecryptAsync(string encryptedText)
        var encryptedBytes = Convert.FromBase64String(encryptedText);
        var decryptionResult = await client.DecryptAsync(keyId, 
                                 JsonWebKeyEncryptionAlgorithm.RSAOAEP, encryptedBytes);
        var decryptedText = Encoding.Unicode.GetString(decryptionResult.Result);
        return decryptedText;

    public async Task<string> EncryptAsync(string value)
        var bundle = await client.GetKeyAsync(keyId);
        var key = bundle.Key;

        using (var rsa = new RSACryptoServiceProvider())
            var parameters = new RSAParameters()
                Modulus = key.N,
                Exponent = key.E
            var byteData = Encoding.Unicode.GetBytes(value);
            var encryptedText = rsa.Encrypt(byteData, fOAEP: true);
            var encodedText = Convert.ToBase64String(encryptedText);
            return encodedText;

Here are a few points about the code that may not be obvious.

First, notice the EncryptAsync method fetches an RSA key from Key Vault and executes an encryption algorithm locally. Key Vault can encrypt data we post to the vault via an HTTPS message, but local encryption is faster, and there is no problem giving a system access to the public part of the RSA key.

Secondly, speaking of public keys, only the public key is available to the system. The API call to GetKeyAsync doesn’t return private key data. This is why the DecryptAsync wrapper method does use the Key Vault API for decryption. In other words, private keys never leave the vault, which is one reason to use Key Vault for decryption instead of bringing private keys into the process.

Setup and Authenticating

The steps for creating a vault, creating a key, and granting access to the key for an application are all steps you can find elsewhere. Once those steps are complete, we need to initialize a KeyVaultClient to give to our wrapper. In ASP.NET Core, the setup might look like the following inside of ConfigureServices.

services.AddSingleton<IKeyVaultCrypto>(sp =>
    AuthenticationCallback callback = async (authority,resource,scope) =>
        var appId = Configuration["AppId"];
        var appSecret = Configuration["AppSecret"];
        var authContext = new AuthenticationContext(authority);
        var credential = new ClientCredential(appId, appSecret);
        var authResult = await authContext.AcquireTokenAsync(resource, credential);
        return authResult.AccessToken;
    var client = new KeyVaultClient(callback);
    return new KeyVaultCrypto(client, Configuration["KeyId"]);

In the above code we use an application ID and secret to generate an access token for Key Vault. In other words, the application needs one secret stored outside of Key Vault to gain access to secrets stored inside of Key Vault. In a future post we will assume the application is running inside of Azure and remove the need to know a bootstrapping secret. Otherwise, systems requiring encryption of the bootstrap secret should use a DPAPI library, or for ASP.NET Core, the Data Protection APIs.


Now that we know how to decrypt secrets with private keys in Key Vault, the application no longer needs to store a decryption key for the connection string. 

var encyptedSecret = keyVault.GetSecret("dbcredentials", appId, appSecret);
var connectionString = keyVault.Decrypt(encryptedSecret, decryptionKeyId);

We'll continue discussing this scenario in future posts.

Gravatar Micah B. Friday, March 9, 2018
Two questions 1. When would you use Key Vault over using Azure's App Service override the configuration keys in Azure. 2. In development, I often connect to services with secrets whether connection strings or API keys. How do I practically keep these secrets from being checked-in to source control, other than just making sure I never check in the web.config (which is highly error prone especially on a team)?
Gravatar Micah B. Friday, March 9, 2018
I should have specified my project is .NET 4.6.2, not Core. To the second question, I found a decent answer with using the file attribute in the appSettings node which overrides the values <appSettings file="../../../../Secrets/MyApp/app.secret.config"> which overrides and adds new values to the app settings. I can even store it in a location outside of the project to ensure it doesn't get checked in. For .NET core, I found a similar approach called "app secrets".
Gravatar scott Friday, March 9, 2018
@Micah: Yes, that's a good approach. I'd use KeyVault over AppService config settings for non-trivial projects. Key Vault gives you an inventory of all secrets and you can grant only the access needed (more details next week).
Gravatar Nen Monday, March 12, 2018
Are the KeyVault's HSMA keys supported for encrypting locally ?
Gravatar scott Monday, March 12, 2018
@Nen: You can grab the public key to use for local encryption. Decryption with the private key is inside of Key Vault.
Friday, March 16, 2018
Gravatar Efren Rodz Tuesday, April 17, 2018
Are we assuming here that the value to encrypt/decrypt is a symmetric key we are using to encrypt/decrypt our actual secrets (conn string, password, etc)? Or are we using the key we have stored in key vault (which is RSA and asymmetric) to encrypt/decrypt our secrets directly without an intermediary symmetric key? I'm a little confused because you mention that RSA keys encrypt small amounts of data and we wouldn't use it to encrypt/decrypt secrets but we would use it instead for the symmetric key that encrypts/decrypts the secrets. If we are doing the former, could you point to a reference that also implements the use of a symmetric key to encrypt the secrets? Thanks.
Gravatar scott Tuesday, April 17, 2018
@efren: The idea is to use the asymmetric key in key vault to encrypt and decrypt a symmetric key. The symmetric key is the one that will encrypt and decrypt connection strings and other secrets in the vault. And yes, that's because a 2048 bit RSA key can only work with 256 bytes of data (minus padding, so effectively 245 bits). With .NET you can do symmetric key encrpytion by combining a CryptoStream with an algorithm class like RijndaelManaged. There are examples in the algorithm classes, for example: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rijndaelmanaged?view=netframework-4.7.2#examples Hope that helps.
Comments are closed.