Encrypting AppSettings with the DynamoDbAppSettings provider

Is it possible to use encrypted AppSettings via DynamoDbAppSettings? We have a requirement that we must store all sensitive data, like connection strings, in an encrypted form. This can be a real pain, and I was hoping DynamoDbAppSettings would help make life easier.

Thanks

No, none of the Configuration providers include support for Encryption out-of-the-box. Maybe you can use the handy encryption utils in CryptUtils.cs to easily encrypt/decrypt text.

IMO your best approach would be to create a custom DynamoDbAppSettings that transparently decrypt/encrypt each setting.

Thanks, I think your idea about a custom DynamoDbAppSettings is a good one. I created a new class called EncryptedDynamoDbAppSettings and modified the Get and Set methods as shown below:

public string Get(string key)
        {
            ConfigSetting config = db.GetItem<ConfigSetting>(key);
            if (config != null && config.Value != null)
                return config.Value.Decrypt();

            return null;
        }

and

public void Set<T>(string key, T value)
        {
            string s = value as string;
            string textValue = s != null
                ? (string)(object)value
                : value.ToJsv();

            if (!textValue.IsNullOrEmpty())
                textValue = textValue.Encrypt();

            db.PutItem(new ConfigSetting { Id = key, Value = textValue });
        }

So the new class is very similar. I used the same tests, modified for our xunit/AutoFixture set up and all tests passed. Are there any other things you can think of that might bite me later?

No that should be fine.

As a follow up to this, I’ve found that the code is working just fine, but I’ve not dealt with the private/public key situation properly. We have one solution with several projects, two of which will be on the same server, and two others which will be on an Admin server behind our firewall. I added the EncryptedDynamoDbAppSettings class from above to one of the projects, and Admin command-line tool that reads an appsettings file and encrypts the appsettings in DynamoDb. That works fine. However, when I try to use the appsettings on my other server, I don’t have the private key so decryption obviously fails. I am generating the keys in the constructor (in the Admin Tool) as shown:

public EncryptedDynamoDbAppSettings(IPocoDynamo db, bool initSchema = false)
        : base(new EncryptedDynamoDbSettings(db))
    {
        Db.RegisterTable<ConfigSetting>();
        this.metadata = db.GetTableMetadata<ConfigSetting>();

        if (initSchema)
            InitSchema();

        RsaUtils.KeyLength = RsaKeyLengths.Bit4096;
        RsaUtils.DefaultKeyPair = RsaUtils.CreatePublicAndPrivateKeyPair();
    }

Can I deal with my issue in the ServiceStack space, or is this standard RSA stuff (which I know little about) and requires some sort of separate server for the key?

You definitely don’t want to be creating a new Public/Private key each time as you would lose anything that was encrypted with any previous key pair. I also don’t think you should use RSA as you’re not trying to communicate with 3rd Parties, you just want to encrypt/decrypt your own data so using AES would be more suitable. Each server would just need a copy of the AES Key to be able to encrypt/decrypt the same data.

You’ll want to read an overview on both Asymmetric Encryption like RSA which uses a public/private key pair, and Symmetric Encryption like AES which can be encrypted and decrypted with the same key that’s both faster than RSA and also suitable for encrypting large payloads. I think the docs in the Encrypted Messaging provides a good overview since it uses the strengths of both RSA and AES to communicate large payloads efficiently.

Basically I’d recommend using AES here since Asymmetric Key Encryption isn’t required. You can create a new AES key with:

byte[] key, iv;
AesUtils.CreateKeyAndIv(out key, out iv);

The IV acts like a password salt and can be public, so after you generate it once you can hard code it in your code-base for instance. You’d then use it along with your key to encrypt/decrypt your AppSettings which you can do with:

var encryptedText = AesUtils.Encrypt(textValue, key, iv);

Which you can then decrypt with:

var textValue = AesUtils.Decrypt(encryptedText, key, iv);

Thanks again for your quick reply. Sine we are using AWS (thanks for the AWS integration by the way), I implemented my EncryptedDynamoDbAppSettings using AWS Key Management Services, and built Encrypt/Decrypt extension methods as (almost) drop in replacements for the RsaUtils methods. The AWS KMS is AES based, so it fits in with your guidance above. Here are the methods:

public static class KmsUtils
{
    public static string KmsEncrypt(this string stringToEncrypt, string arn)
    {
        using (AmazonKeyManagementServiceClient kms = new AmazonKeyManagementServiceClient())
        {
            EncryptResponse encryptResponse;
            using (MemoryStream ms = new MemoryStream())
            {
                StreamWriter sw = new StreamWriter(ms);
                sw.Write(stringToEncrypt);
                sw.Flush();
                encryptResponse = kms.Encrypt(new EncryptRequest
                {
                    KeyId = arn,
                    Plaintext = ms
                });
            }
            return Convert.ToBase64String(encryptResponse.CiphertextBlob.ToArray());
        }
    }

    public static string KmsDecrypt(this string encryptResponse)
    {
        
        string returnVal;

        using (AmazonKeyManagementServiceClient kms = new AmazonKeyManagementServiceClient())
        {
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(encryptResponse)))
            {
                DecryptResponse decryptResponse = kms.Decrypt(new DecryptRequest
                {
                    CiphertextBlob = ms
                });
                returnVal = Encoding.UTF8.GetString(decryptResponse.Plaintext.ToArray());
            }
        }
        return returnVal;
    }
}

I’m not totally excited about passing in the AWS ARN for the key to the encrypt method, but I didn’t want to embed it in the function either, so I have it as a constant in a static class.

1 Like

I am experiencing an issue with using DynamoDB for our AppSettings, and am starting with ServiceStack first because I don’t know where the problem lies. I implemented the solution above, and put it in an administrative command line app we have. Everything works fine; I get a table named after my class (configSettings) and it is properly populated with AppSettings (read from a test file) using a Tier, so I have things like:

Development.ConnectionString MyDevelopmentConnectionString
Production.ConnectionString MyProductionConnectionString

Except that the values are encrypted in the DynamoDB table. However, over time, and I’m not sure how much, we get weird items in the table that look like this:

It may well be something in our code, but our code, in a separate app outside of the Admin app, is only supposed to read the values, not write. Have you seen anything like this?

Those keys look suspiciously like the keys in the App Settings Tests. So I’m assuming you’ve run the tests against your AWS DynamoDB instance.

Oy, yes, that was it. Thank you, I was losing my mind…

1 Like