Thomas K
Thomas K

Reputation: 64

External app config and encryption of settings

We have a number of applications and services that need to share an external app config file. The external file contains a configSection with information that we which to encrypt. Each service and application resides in its own application folder, and that is where the problems begin to escalate. In the App.config, external files can be referenced with either ‘configSource’ or ‘file’ attribute. ‘configSource’ cannot be used because the external config file does not reside in the app folder or in app sub folders. Threrefore we have to use the ‘file’ attribute.

<customSettings file=”path to setting”/>

The ‘customSettings’ configSection has been defined as followed:

<configSections>
    <section name="customSettings" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</configSections>

I’m then trying to encrypt the configSection using code like this:

Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection("customSettings");

if (!section.SectionInformation.IsProtected)
{
   section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
   config.Save();
}

Because I’m using the file attribute (I suspect). The config section is encrypted in App.config and the external file is not. What gets encrypted in App.config is just

<customSettings file=”path to setting”/>

.Which is pretty useless.

This means that the Individual applications and services cannot encrypt the external config file. I then had the idea that I would place a small application residing in the same directory as the external config file. The purpose of this application was to encrypt the external config file by using ‘configSource’ attribute instead. This approach does not work at all. Nothing happens, and nothing is encrypted.

To investigate a little further I placed the ‘customSettings’ in the App.config and encrypted the section successfully. I then copied the encrypted data to the external file to test if encryption could work in the external config file. This works fine with the ‘configSource’ but throws an exception when using the ‘file’ attribute.

Exception thrown:

Unrecognized attribute 'configProtectionProvider'

Since we must use the ‘file’ attribute in the app.config I now have 2 problems.

  1. Cannot encrypt external file.
  2. If the external file is manually encrypted, I cannot read it using the ‘file’ attribute.

Upvotes: 0

Views: 1457

Answers (2)

Thomas K
Thomas K

Reputation: 64

After a lot of research and looking into the code for NameValueFileSectionHandler i realized that the class was not able to resolve configSection's pointed to by the file="file path" attribute, if the external configSection where encrypted. Don't know if this was a bug or not in NameValueFileSectionHandler. Maybe someone here can answer that.

However I ended up writing my own NameValueFileSectionHandler which could return a NameValueCollection and handle encrypted external config files.

public class NameValueFileProtectedSectionHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, XmlNode section)
    {
        object result = parent;

        XmlNode fileAttribute = section.Attributes.RemoveNamedItem("file");

        if (fileAttribute == null && fileAttribute.Value.Length == 0)
        {
            return new NameValueSectionHandler().Create(result, null, section);
        }

        IConfigErrorInfo configXmlNode = fileAttribute as IConfigErrorInfo;

        if (configXmlNode == null)
        {
            return null;
        }

        string directory = Path.GetDirectoryName(configXmlNode.Filename);
        string absoluteFilePath = Path.GetFullPath(directory + fileAttribute.Value);

        if (!File.Exists(absoluteFilePath))
        {
            throw new ConfigurationErrorsException(string.Format("external config file: {0} does not exists", absoluteFilePath));
        }

        var configXmlDocument = new ConfigXmlDocument();
        try
        {
            configXmlDocument.Load(absoluteFilePath);
        }
        catch (XmlException e)
        {
            throw new ConfigurationErrorsException(e.Message, e, absoluteFilePath, e.LineNumber);
        }

        if (section.Name != configXmlDocument.DocumentElement.Name)
        {
            throw new ConfigurationErrorsException(string.Format("Section name '{0}' in app.config does not match section name '{1}' in file '{2}'", section.Name, configXmlDocument.DocumentElement.Name, absoluteFilePath));
        }

        var nodeToDecrypt = configXmlDocument.DocumentElement["EncryptedData"];

        if (nodeToDecrypt == null)
        {
            throw new ConfigurationErrorsException(string.Format("External encrypted file {0} does not contain EncryptedData element", absoluteFilePath));
        }

        var protectionProvider = new DpapiProtectedConfigurationProvider();
        var decryptedConfigSection = protectionProvider.Decrypt(nodeToDecrypt);

        result = new NameValueSectionHandler().Create(result, null, decryptedConfigSection);

        return result;
    }
}

The handler is restricted to default configuration encryption. But I could imagine that it would be possible to extend the Create function to support custom providers as defined in the app.config file.

Upvotes: 0

BDarley
BDarley

Reputation: 1255

An option you could take is to create a custom config section, avoid using the appsettings section and rely on the custom config section. You'll need to create your own handler to read in the external file of course.You could point to any file at that point and the entire file could be obfuscated and not conform to XML standard

Or you could add a setting in app settings that points to the encrypted file and read in the file manually

Upvotes: 1

Related Questions