Cloud Security Pattern: Secure Application Secrets with Secret Encryption Key using Azure Key Vault and RSA HSM

Previous two post explained how to Setup Key Vault in Azure and Access Azure Key Vault using Azure AD Application and Certificates. This blog post would discuss Cloud Security Patterns (or Application Cryptography Patterns) using Microsoft Azure Key Vault.

Two most prominent challenges that any cloud deployment would demand is securing your application configuration or application secrets. i.e. database connection strings, third party API keys, passwords, encryption salts, unsecured endpoints. Cyber forensic evidence suggests that compromised application configuration leads to larger and abysmal organisation security failures. Therefore, it is crucial for every cloud project to consider the aspect seriously. OWASP finds A5 Security Misconfiguration and A6 Sensitive Data Exposure  are two most common vulnerability.

The suggested cloud application security patterns, secure Application Secrets using random AES 128-bit symmetric data key, and wrap secure symmetric data key using Application specific RSA HSM (HSM Key). All access points are secured by Azure AD Security Principal and Client Certificates.

  • Azure AD Service Principal based Client Assertion Certificate to authenticate with Key Vault (Perimeter Security),
  • Application Secret is encrypted by random AES 128-bit symmetric key.
  • Random AES 128-bit symmetric key is protected by RSA HSM key.

The recommended security pattern never persist any of the secret encryption key other than Azure Key Vault. To improvise the pattern, you can use supported Managed PKI service providers (i.e. DigiCert, GlobalSign, WoSign) with Azure. The Managed PKI Provider would generate RSA key and add into your Azure Key Vault (your organisation never receive PFX or PVK files).

The security pattern has two applications,

CI/CD pipeline ensure updating application secrets securely in the Azure Key Vault, while Application can only read these secrets. The key objectives of the patterns are,

  • Never store secret Keys or Certificate at Rest.
  • ACL protection (through Client Assertion Certificate) with the Azure Key Vault.
  • Secret Key (AES 128-bit) Encryption Key not exportable, and managed and generated by Public CA and Azure Key Vault.

Keys & X509 Certificates

There are four certificate and keys are involved in the security pattern.

  1. CAC: Client Assertion Certificate, an X509 Certificate to authenticate using AD RM Service Principal for accessing Key Vault resources and functions. The certificate can be stored at local Windows Certificate Store.
  2. SEK: Secret Encryption Key, it is an AES 128-bit encryption key to encrypt the secret itself. AES 128-bit is very powerful against Brute Force Attack. A-Z and 0-9 means 36 possible characters, 20 such characters imply 3620 possible keys. That’s approximately equal to 2103.4. It would take billions of years to generate the correct permutation. Secondly, the key is randomly generated thus every time the deployment cycle would replace the current SEK to new random SEK.
  3. SEK-EK: Secret Encryption Key – Encryption Key, it is RSA-HSM Asymmetric Key (Public-Private Pair). The key is generated through Managed PKI or outside of the CD/CI ecosystem. The key would be saved in Key Vault HSM (HSM Protected Key). The only purpose of the key is to wrap and unwrap SEK.
  4. Wrapped-SEK: CD/CI pipeline wrap SEK using SEK-EK (HSM Protected Key).

Continuous Integration and Continuous Deployment Secure Secret Pattern

Generic Continuous Integration and Continuous Deployment UML sequence diagram for the secure secret pattern. The objective of sub-pattern are,

  • Securely update and manage cloud application secrets.
  • Safe-guard local copy of application secrets.

Before, we get started with the sequence diagram. I would like to mention some good candidate for Local Storage Repository,

  • SQL Server Always Encrypted
  • TDE + Encrypted Azure Blob Storage
  • Local Source Control (Encrypted) File

I would not like to dive deep into comparison, but these all options are comparably secure than plain text configuration. Let us now begin with the sequence diagram,

1. Get CAC (Client Assertion Certificate) through Certificate Thumbprint

The thumbprint of the certificate can be store in the local configuration file. If you are not sure how to find the thumbprint of stored certificate, try the following command.

Get-ChildItem -path cert:\LocalMachine\My /*Replace with your certificate path*/

Aaron Jensen’s open source project Carbon (Get-Certificate)  would be useful to work with Windows certificate store. For more information refer to previous pervious post Guide: Setup Key Vault using Azure AD Application and Certificates

2. Get Secrets from Local Repository

You need some secure parking place before secrets and configuration are released or deployed into the cloud. It is essential for organisations to make the temporary arrangement safer. Ideally, it could be SQL Server with the Always Encrypted feature, allowing organisations to keep configuration data always encrypted or TDE/Payload Encryption with Azure Blob Storage.

3. Generate Random AES 128-bit Key (SEK)

As discussed before SEK is Symmetric Key and very easy to generate. You can following sample code or equivalent in PowerShell to generate random AES 128-bit key.

public static byte[] GenerateKey(int keySize, int blockSize)
{
    var aesManaged = new AesManaged {KeySize = keySize, BlockSize = blockSize};
    return aesManaged.Key;
}
4. Wrap SEK using RSA-HSM Key

Convert byte[] key into Base64 string, and call HSM Key Vault using API function WrapKeyAsyncRSA-HSM Algorithms  supported by Azure Key Vault.

public async Task<KeyOperationResult> WrapAsync(byte[] symmetricKey, string algorithm)
{
    if (symmetricKey == null) throw new ArgumentNullException(nameof(symmetricKey));
    if (algorithm == null) throw new ArgumentNullException(nameof(algorithm));

    return await _client.KeyVaultClient.WrapKeyAsync(_keyId, algorithm, symmetricKey);
}
5. Encrypt the Secret

Use .NET Cryptography namespace to encrypt the binary data (Secret) using AES 128-bit key (SEK). You can also refer to my Github repository for reference implementation. Github://SimplifiedAzure/KeyVault/KeyVault/Helper/AesEncryption.cs 

6. Add Wrapped-SEK and Algorithm as Tag to Key Vault Secret

Embed Wrapped-SEK and Encryption Algorithm Name using JWE Compact Serialization  guidelines. BASE64URL(Wrapped-SEK) || '.' || BASE64URL(Encryption Algorithm) || '.' || BASE64URL(Encrypted Secret).

7. Add/Update Application Secret

Creating or updating the Key Vault Secret is a very simple task, please refer following code as a reference point.

private async Task<SecretBundle> CreateOrUpdateSecret(string secretName, string secretValue, Dictionary<string, string> tags, string contentType, SecretAttributes secretAttributes)
{
    return async _client.SetSecretAsync(_vaultAddress, secretName, secretValue, tags, contentType, secretAttributes);
}
8. Get SecretId and update Application Configuration

SetSecretAsync method would return SecretBundle, you can get id value (https://***keyvaultname***.vault.azure.net/secrets/***secretname***/***xxxxxguidversionid xxxxx***) by accessing SecretId of the SecretBundle. Persist the SecretId in Application Configuration; the application bootstrap would reverse the same process to obtain Secret Value.

Do not forget to keep all the configuration objects in memory and dispose of the objects as soon as not needed. No sensitive data should be stored at Rest.

Application Secure Secret Pattern

The process is bare reversal of the previous sub-pattern, objectives of the sub-pattern are,

  • Securely retrieve Application Secrets.
  • Cache all Secrets in memory and use for Application Lifecycle.

Let us get started with Application Secure Secret Pattern,

1. Get Client Assertion Certificate from Local Certificate Store or Secure Storage

A very useful snippet to retrieve Certificate by Certificate Thumbprint, or you can optimise by accessing through direct certificate path. Refer step by step guide to Setup Key Vault using Azure AD Application and Certificates implementation guidelines.

public static X509Certificate2 FindCertificateByThumbprint(string certificateThumbprint)
{
    if (certificateThumbprint == null)
        throw new System.ArgumentNullException("certificateThumbprint");

    foreach (StoreLocation storeLocation in (StoreLocation[])Enum.GetValues(typeof(StoreLocation)))
    {
        foreach (StoreName storeName in (StoreName[])Enum.GetValues(typeof(StoreName)))
        {
            X509Store store = new X509Store(storeName, storeLocation);

            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false); // If root is installed then change false to true.
            if (col != null && col.Count != 0)
            {
                for each (X509Certificate2 cert in col)
                {
                    if (cert.HasPrivateKey)
                    {
                        store.Close();
                        return cert;
                    }
                }
            }
        }
    }

    throw new System.Exception(string.Format("Could not find the certificate with thumbprint {0} in any certificate store.", certificateThumbprint));
}
2. Get Secret from Application Key Vault and Extract SEK

Retrieve encrypted secret from Key Vault, extract Wrapped-SEK, Encryption Algorithm and Encrypted Secret. Please see Embed Wrapped-SEK and Encryption Algorithm Name.

Reference, JWE Compact Serialization  , BASE64URL(Wrapped-SEK) || '.' || BASE64URL(Encryption Algorithm) || '.' || BASE64URL(Encrypted Secret).

3. Unwrap Wrapped-SEK

Call UnwrapAsync method to unwrap Wrapped-SEK.

public async Task<KeyOperationResult> UnwrapAsync(byte[] wrappedKey, string algorithm)
{
    if (wrappedKey == null) throw new ArgumentNullException(nameof(wrappedKey));
    if (algorithm == null) throw new ArgumentNullException(nameof(algorithm));

    return await _client.KeyVaultClient.UnwrapKeyAsync(_keyId, algorithm, wrappedKey);
}

Use .NET Cryptography namespace to decrypt the encrypted binary data (Secret) using AES 128-bit key (SEK). You can also refer to my Github repository for reference implementation. Github://SimplifiedAzure/KeyVault/KeyVault/Helper/AesEncryption.cs 

4. Cache the Secret in Memory

Once you retrieve plain-text secret, the application should keep the secret in memory cache for the lifecycle. Make sure no application secret is stored or cached at Rest.

The above process should take place as Application Bootstrap. Azure Key Vault API are asynchronous and fast, I have tried with 100 encrypted secrets to test the pattern. My performance test bootstrapped the application for 6.1 seconds (UK South Azure Region).

SimplifiedAzure.KeyVault Azure Key Vault Wrapper

If interested in the pattern, then look at my new open source wrapper library.

https://github.com/nilayparikh/SimplifiedAzure

Note: The library is not matured yet for production use. Work in progress with Documentation and Key Vault Patterns & Practices. I would release soon to public domain.

Disclaimer

The views expressed on this site are personal opinions only and have no affiliation. See full disclaimerterms & conditions, and privacy policy. No obligations assumed.