You can use the technique used by Azure storage to encrypt data at rest (envelope method): https://docs.microsoft.com/en-us/azure/storage/storage-client-side-encryption
KeyVault has the ability to Wrap / Unwrap (encrypt / decrypt) symmetric keys so that they are safe to store along with your encrypted data.
Here are the basic steps:
- Generate an AES key (256 bit, CBC mode) using RNGCryptoServiceProvider
- Encrypt data (credentials)
- Save initialization vector (IV). You can simply concatenate it with a byte array of ciphertext for later searching, when you want to decrypt - IV does not need protection.
- Wrap (encrypt) the AES genetic symmetric key with the KeyVault key.
- Save Wraped AES Key, IV, CipherText and Key Version (GUID at the end of the URI in KeyVault).
- Make sure you grant Wrap / Unwrap permissions to KeyVault to register applications created in Azure AD. Use the client ID / application ID + key or pfx to log in to Azure in GetToken ().
You will need these nuget packages:
Install-Package Microsoft.Azure.KeyVault Install-Package Microsoft.Azure.KeyVault.Extensions Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.16.204221202
Get a link to KeyVaultKeyResolver
KeyVaultKeyResolver cloudResolver = new KeyVaultKeyResolver(Utils.GetToken); // Example GetToken implementation public class Utils { // Retrive JWT token to be used for KeyVault access. internal async static Task<string> GetToken(string authority, string resource, string scope) { var authContext = new AuthenticationContext(authority); // Could use pfx instead ClientCredential clientCred = new ClientCredential( ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["clientSecret"]); AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); if (result == null) throw new InvalidOperationException("Failed to obtain the JWT token."); return result.AccessToken; } }
Once you have KeyResolver, you can get the IKey for the Wrap / Unwrap symmetric AES key as follows:
Wrap / Encrypt AES Key
The key identifier is the URI from Key Vault, and aesKey is the byte [] of your AES key for encryption:
// Resolve an IKey by Key ID from URI in KeyVault var keyEncryptionKey = cloudResolver.ResolveKeyAsync(keyId, CancellationToken.None).GetAwaiter().GetResult(); // Take our gen'ed AES Key and wrap (encrypt) it. Tuple<byte[], string> wrappedKey = keyEncryptionKey.WrapKeyAsync(aeskey, null /* algorithm */, CancellationToken.None).GetAwaiter().GetResult();
Byte [] in Tuple contains the encrypted bytes of the symmetric key and the name of the algorithm used. Save them as metadata with your ciphertext.
Expand / Decrypt AES Key
A call using the same key (the key version matters), algony_name is the name of the algorithm used to wrap the key (for example, "RSA-OAEP").
Other details to consider are key backup / restore and key rotation.