Azure Key Vault is a service that allows the secure storage of secrets. It facilitates common key management activites such as key rotation and key generation.
Key Vault is essentially abstraced hardware security modules (HSMs). An HSM is a secure bit of hardware designed to manage keys. They have securty features you wouldn’t find on other bits of hardware, e.g. physical tamper resistence. The HSMs used by Azure Key Vault meet FIPS-140-2 level 2 standards.
I recently implemented app-level encryption of email addresses for a side project. Encrypting values is relatively straight forward - there’s plenty of documentation and examples explaining how to go about it in almost any language. Things get complicated when you start looking at how to keep your encryption keys safe.
Your encrypted data is only as safe as your encryption key. If you lose it - you’ve lost your data. If someone else has it, they can access your data.
Encryption is only the tip of the iceberg - key management is the hard bit.
Encryption Methods
There are 2 relevant encryption methods that I used:
Method | Keys | Speed | Description |
---|---|---|---|
AES | Symmetric | Fast | Data is encrypted and decrypted with the same key |
RSA | Asymmetric | Slow | Data is encrypted with the public key and decrypted with the private key |
Encrypting Encryption Keys
To protect my AES key, I’m going to encrypt it using RSA encryption. This is called key wrapping - the key I’m using to encrypt my AES key in known as a Key Wrapping Key (KEK). Wrapping my AES key provides a few advantages:
- My unencrypted AES key doesn’t have to be stored anywhere
- Key rotation can be facilitated without re-encrypting all of my data
- The RSA private key used to encrypt my AES key never has to leave the boundaries of the HSM.
Even better - I can use the secrets API in Key Vault to store my new wrapped key, so I don’t actually have any encryption keys stored locally with my application.
DIAGRAM HERE
The Code
I implemented this in a .net standard library.
Generating an RSA key
The RSA key we’re going to use to wrap our AES key can be generated in the HSM - the key never has to leave it. This can be done in the portal.
First, select “+ Generate/Import” in your Key Vault:
Next, fill out the form and be sure to select “RSA-HSM”:
Connecting to Azure
First step, we need to new up a key vault client (I’m using the NuGet package provided by Microsoft to access the key vault API).
The ‘GetToken’ parameter passed in is actually a method, as defined below:
This method grabs my client ID and secret (Azure credentials) from a configuration section I defined in my app (
_encryptionConfiguration.Value), and uses them to get a token to access the key vault API.
Generating and wrapping a key
Our key vault client is now in a state where it’s ready to access key vault.
The code below provides an example of how to do this.
- First, we can make use of the System.Security.Cryptography namespace to generate a new AES key.
- Second, we call the WrapKey function on the key client. This encrypts our key within the confines of the HSM - the private RSA key never leaves the HSM
- Finally, we store our wrapped key in key vault using the secrets API - calling SetSecretAsync on key vault
Be careful - generating keys securely is difficult and needs to be planned. The code above is just to outline the theory - keys should be generated in a secure environment and recovery strategies should be documented.
Unwrapping the key
As I mentioned before, I’ve saved my wrapped AES key in key vault using the secrets API. We can get this wrapped key like this:
Once we have the wrapped key, we can call a method on the key vault client to unwrap this key:
_encryptionConfiguration.Value.RSAKeyis an identifier of the RSA key used to wrap the AES key.
This is what the complete code might look like (error handling and null checks omited for brevity):
Is it secure yet?
So far so good - we’re got our AES key wrapped, and it’s safely in key vault. However, now we have our Azure credentials (that have permissions to unwrap our key) sitting in our configuration file. Security is never an absolute state, however we can take steps to improve the situation:
- Encrypt the configuration section using aspnet_regiis (system admins will still be able to access this)
- Authenticate with Azure using certificates (your certificate store still needs to be secure!)