Monday, 7 September 2015

Reading encrypted content in external configuration file

.NET framework has a great configuration feature - external configuration files/external configuration source. It allows you to move certain configuration sections or parts of appSettings to an external file and then mention the path of external configuration file in the original configuration file. .NET framework merges the two configuration files and its ConfigurationManager API works seamlessly. Till you encrypt the external configuration file :).
 
So what is the solution? Custom code, anyone? Under the hood, .NET framework uses DpapiProtectedConfigurationProvider to some extent to read/write encrypted data. We can use the same along with ConfigXmlDocument. Here is one function that I wrote to read appSettings value from external configuration file that may or may not be encrypted:
 
public static class CommonConfigurationProvider
    {
        public static string GetAppSettingsValue(string key, string configurationFilePath)
        {
            ConfigXmlDocument configXmlDocument = new ConfigXmlDocument();
            configXmlDocument.Load(configurationFilePath);
 
            XmlNode appSettingsNode = null;
            if (configXmlDocument.DocumentElement.LocalName == "appSettings")
            {
                appSettingsNode = configXmlDocument.DocumentElement;
            }
            else
            {
                appSettingsNode = configXmlDocument.DocumentElement.SelectSingleNode("appSettings");
            }
 
            if (appSettingsNode == null)
            {
                throw new Exception("No appSettings section found!!");
            }
 
            XmlNode settingNode = null;
            if (appSettingsNode.Attributes["configProtectionProvider"] != null)
            {
                DpapiProtectedConfigurationProvider p = new DpapiProtectedConfigurationProvider();
                settingNode = p.Decrypt(appSettingsNode.SelectSingleNode("EncryptedData"));
            }
            else
            {
                settingNode = appSettingsNode;
            }
 
            if (settingNode != null)
            {
                var valueNode = settingNode.SelectNodes("add");
                if (valueNode != null)
                {
                    for (int i = 0; i < valueNode.Count; i++)
                    {
                        if (valueNode[i] != null && valueNode[i].Attributes["key"].InnerText == key)
                        {
                            return valueNode[i].Attributes["value"].InnerText;
                        }
                    }
                }
            }
 
            return null;
        }
    }
 
 
You can use both relative or absolute file path. Using this method is quite straightforward.
 
Console.WriteLine(ConfigurationManager.AppSettings["TestKey1"]);
            Console.WriteLine(CommonConfigurationProvider.GetAppSettingsValue("TestKey2", @"ExternalConfig\app.config"));
            Console.WriteLine(CommonConfigurationProvider.GetAppSettingsValue("TestKey3", @"ExternalConfig\app.config"));
The only caveat that I found was that it did not behave seamlessly. Of course, we can write a wrapper on ConfigurationManager class that takes care of that and makes usage seamless. Shouldn't be that hard.

No comments:

Post a Comment