Using Azure Key Vault for storing username and password

azure, azure-keyvault

Solution

You can use the technique that Azure blob storage uses to encrypts data at rest (envelope method): https://learn.microsoft.com/en-us/azure/storage/storage-client-side-encryption

KeyVault has the ability to Wrap / Unwrap (encrypt/decrypt) symmetric keys so they are safe for you to store along with your encrypted data.

Here are the general steps:

- Generaete an AES key (256 bit, CBC mode) using RNGCryptoServiceProvider

- Encrypt the data (credentials)

- Save the Initialization Vector (IV). You can just concat it to the ciphertext byte array for retrieval later when you want to decrypt - IV doesn't need protected.

- Wrap (encrypt) the genereated AES Symmetric Key using the Key in KeyVault.

- Store Wraped AES Key, IV, CipherText, and Key Version (GUID at the end of the URI in KeyVault).

- Make sure you grant the Wrap / Unwrap permissions in KeyVault to the Application Registration created in Azure AD. Use the Client Id / Application ID + Key or pfx to auth to Azure in GetToken().

You'll 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 ref 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 have KeyResolver, you can get an IKey to Wrap / Unwrap your AES symmetric key as follows...

Wrap / Encrypt AES Key

The keyID is the URI from Key Vault and aesKey is the byte[] of your AES key to encrypt:

// 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();

The byte[] in the Tuple contains the encrypted bytes of the symmetric key and the name of the algorithm used. Save these as meta data with your ciphertext.

Unwrap / Decrypt AES key

Call using the same key (key version matters), algoName is the name of the algorithm used to wrap the key (e.g. "RSA-OAEP").

// Retrieve the IKey by Key ID
// Unwrap Key
byte[] aesKey = rsa.UnwrapKeyAsync(wrappedKeyBytes, algoName, CancellationToken.None).GetAwaiter().GetResult();

Other details to think about are the Key backup/recovery and key rotation.

Problem

I am working on an app where I want to store username and password for service account which will be used by a daemon service. The idea is to provide application administrator a dashboard where he / she can enter credentials for service account and later it can be stored somewhere safe. I can think of storing it in secure place like Azure Vault and get it from there whenever required. However, key and secret are different entities in Azure Vault. I can not store them somewhere as a combination. Has anyone done that before? Or is there any better alternative to store credentials in Azure?

Original source