6

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?

Rahul Patil
  • 5,656
  • 6
  • 37
  • 65
  • Just curious, is the `service account` you talk about is for managing user's Azure Subscription on their behalf? – Gaurav Mantri Oct 20 '16 at 04:02
  • Nope @GauravMantri. Those will be credentials for Dynamics CRM service account. The account will be used to update entities in Dynamics CRM through a daemon app. Btw, Good work there on blogs and articles, I do follow your blogs ;-) – Rahul Patil Oct 20 '16 at 04:04
  • Service account username and password will be entered by the admin, so what is the value of adding them again into azure key vault? – Haitham Shaddad Oct 20 '16 at 05:03
  • @HaithamShaddad I want to store it securely somewhere. Not in sql table obviously :-)\ – Rahul Patil Oct 20 '16 at 05:35
  • Azure key vault is designed to hold secrets that will be used in applications, In windows, IIS, service account is configured at the application pool level and you never need its password after that. – Haitham Shaddad Oct 20 '16 at 05:38

1 Answers1

7

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:

  1. Generaete an AES key (256 bit, CBC mode) using RNGCryptoServiceProvider
  2. Encrypt the data (credentials)
  3. 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.
  4. Wrap (encrypt) the genereated AES Symmetric Key using the Key in KeyVault.
  5. Store Wraped AES Key, IV, CipherText, and Key Version (GUID at the end of the URI in KeyVault).
  6. 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.

m0nty
  • 101
  • 2
  • 5