Click here to Skip to main content
15,879,096 members
Articles / Web Development / IIS

Encrypt/Decrypt Internal and External Sections

Rate me:
Please Sign up or sign in to vote.
4.69/5 (5 votes)
5 Mar 2010CPOL5 min read 32K   324   19   5
Additional to web.config internal sections encryption/decryption, this tool shows how to encrypt/decrypt external sections in config files.

Introduction

Recently I was asked to encrypt several sections of a web.config file. But our configuration resides in sections that are implemented in external config files referenced from within the web.config, with the configSource attribute.

There is a lot of help on the web, about how to encrypt or decrypt either with a tool or programmatically a section, but all these methods that are basically the same, simply fail to process the external files without even producing an error message.

Background

During the development of the project before I got involved, all configuration was stored in the AppSettings and ConnectionStrings sections or within custom sections, always in the web.config file. For maintenance and easy configuration when I was involved, I decided to split and organize all the above configuration values in various sections either custom or based on the NameValue template.

I additionally decided that those sections should be implemented in external files, referenced from the web.config file. I don't believe that there is a point in me explaining the benefits of the above differences, but for me an additional flexibility for creating automated deployment procedures as always good.

I always like core development so everything I create is usually located in framework projects that provide a clean and out of the box functionality for other developers in the same organization. Because the framework code has grown a lot, I created a stripped down version project that contains the source files needed for this article only. I'm saying this because the complexity of the project may seem a little odd, but keep in mind that several parts are used for other functions that are not visible here.

Help on the tools or the API to encrypt or decrypt configuration files can be easily found by Googling for it.

Few Words About the Code

The code that does the actual encryption and decryption is located with the BaseConfigEncryption that will hopefully be able to manage either web.config files or app.config files. At present, only web.config files are supported, the functionality of which is provided by its child class WebConfigEncryption.

The EncryptionStartParameters is a generic based class that inherits the functionality of handling the string[] args parameters of a main function. Basically, the idea is to create a typed instance of what was asked, through the command argument list. It also handles arguments when the process is being executed as a Smart Client.

In order to encrypt or decrypt a config, you need the path of the application that has the targeted config file, the encryption provider for example DataProtectionConfigurationProvider and the sections that we wish to encrypt. The same is required for decryption, but no provider is needed.

Arguments Examples

Sample examples of arguments are:

  • For encryption:
    -e -p="D:\Web Sites\CCMS_EFG_Prod\telamon-CCMS" 
    -sp=DataProtectionConfigurationProvider -s=DatabaseSection 
  • For decryption:
    -d -p="D:\Web Sites\CCMS_EFG_Prod\telamon-CCMS" -s=DatabaseSection 

Keep in mind that the command line arguments handler is not designed to handle arguments as the several DOS commands and other tools do, but that is not the subject here.

Implementation

BaseConfigEncryption does all the stuff with help from some other helper functions.

First of all, we must load the config file. This is a little tricky but overridable GetConfiguration does the job returning a Configuration object, thus the base class does not need to know if it is processing web or normal application configuration.

C#
protected override System.Configuration.Configuration GetConfiguration()
{
WebConfigurationFileMap wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", 
	new VirtualDirectoryMapping(base.RootPath, true, "web.config"));
return WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
}

At this point, the configuration has been loaded and we must encrypt or decrypt it.

The idea is this. Regardless of whether we are encrypting or decrypting, for each section that is required, we check whether that section is implemented within the root config file, such as web.config or within an external file.

C#
foreach (string sectionName in sections)
{
ConfigurationSection section = configuration.GetSection(sectionName);
if (!String.IsNullOrEmpty(section.SectionInformation.ConfigSource))
{
this.sectionsWithExternalFiles.Add(sectionName);
}
}

So first business in order is to find all sections that are implemented in external files and populate them in sectionsWithExternalFiles. For each of these sections, the idea I had was to copy the implementation from the external file in the root config file. This is done with the function MoveSectionsFromExternalFile that iterates MoveSectionFromExternalFile.

C#
private void MoveSectionsFromExternalFile()
{
if (this.sectionsWithExternalFiles.Count == 0)
{
return;
}
XDocument xDocument = XDocument.Load(ConfigFilePath);
XElement xRoot = xDocument.Root;
string nameSpace = xDocument.Root.GetDefaultNamespace().NamespaceName;
foreach (string sectionName in this.sectionsWithExternalFiles)
{
MoveSectionFromExternalFile(xDocument.Root.Element
	(XName.Get(sectionName, nameSpace)), sectionName);
}

xDocument.Save(ConfigFilePath);
ClearXmlNS(ConfigFilePath);
}

private void MoveSectionFromExternalFile(XElement xSection,string sectionName)
XAttribute xConfigSource=xSection.Attribute("configSource");
string configFile = xConfigSource.Value;
this.externalFiles.Add(sectionName,configFile);
XDocument xConfigFile = XDocument.Load(Path.Combine(RootPath, configFile));
xConfigSource.Remove();
xSection.Add(xConfigFile.Root.Attributes());
xSection.Add(xConfigFile.Root.Elements());
ClearXmlNS(xSection);
}

During the above process, the externalFiles is populated with the section name and file name pair that will be used later for the same but reverse procedure.

At this point, we have a web.config file that has been merged with all the needed external files so the only thing to do is use the API mentioned and found by Google search engine.

For each section, we check whether it is encrypted or not and based on whether we are encrypting or decrypting, we call the appropriate command.

C#
foreach (string sectionName in sections)
{
ConfigurationSection section = configuration.GetSection(sectionName);
switch (String.IsNullOrEmpty(protectionProvider))
{
case false:
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection(protectionProvider);
}
break;
case true:
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
}
break;
}
}

After that, we just have to move the section information from the root config file to the external files and reapply the configSource attribute for these sections. To do this, we call MoveSectionsToExternalFile that iterates MoveSectionToExternalFile for each section.

C#
private void MoveSectionsToExternalFile()
{
if (this.sectionsWithExternalFiles.Count == 0)
{
return;
}
XDocument xDocument = XDocument.Load(ConfigFilePath);
XElement xRoot = xDocument.Root;
string nameSpace = xDocument.Root.GetDefaultNamespace().NamespaceName;
foreach (string sectionName in this.sectionsWithExternalFiles)
{
MoveSectionToExternalFile(xDocument.Root.Element
	(XName.Get(sectionName, nameSpace)), sectionName);
}
xDocument.Save(ConfigFilePath);
ClearXmlNS(ConfigFilePath);
}
private void MoveSectionToExternalFile(XElement xSection,string sectionName)
{
string configFile = this.externalFiles[sectionName];
XDocument xConfigFile = XDocument.Load(Path.Combine(RootPath, configFile));
xConfigFile.Root.RemoveAll();
xConfigFile.Root.Add(xSection.Attributes());
xConfigFile.Root.Add(xSection.Elements());
ClearXmlNS(xConfigFile.Root);
string configFilePath = Path.Combine(RootPath, configFile);
xConfigFile.SaveWithoutHeader(configFilePath);
ClearXmlNS(configFilePath);
xSection.RemoveAll();
xSection.Add(new XAttribute(XName.Get("configSource"), configFile));
}

All section transfers are one with the help of LINQ TO XML which is far better than the previous mechanism of accessing XML documents. The only trouble I got was with the namespaces in various nodes that were embedded in the edited XML. I believe it had something to do with the namespace declaration in the original config file.

After some search, I solved this problem by telling each element that its namespace name was empty and that left me with an xmlns="" in the saved files. I then loaded the files, and replaced these literals with nothing and everything was ok. The functions that did this cleaning up are:

C#
private void ClearXmlNS(XElement xElement)
{
foreach (XElement e in xElement.DescendantsAndSelf())
{
if (e.Name.Namespace != XNamespace.None)
{
e.Name = XNamespace.None.GetName(e.Name.LocalName);
}
}
}
private void ClearXmlNS(string filePath)
{
string text = File.ReadAllText(filePath);
text = text.Replace("xmlns=\"\"", "");
File.WriteAllText(filePath,text);
}

If you do not do the cleanup, then the namespaces are encrypted and the transparent decryption of the System.Configuration throws an exception. It does so even with the blank namespace names. I know this may not be the best way, but I couldn't find a better way in the short time I had.

Points of Interest

During the development of this tool, there are some assumptions made.

  • Because we have a specific pattern in naming the files according to the section names, there may be a problem if that pattern is not followed. I believe that it won't matter, but since I had that in mind while creating the tool, it is best for you to know that.
  • Custom encryption providers are not supported, because I was not interested in them. I was creating a tool solely based on our project needs. In the future if a requirement is made, possibly it will be added.
  • The tool has been tested only for the DataProtectionConfigurationProvider provider.
  • The tool has a /? argument that produces a help. The same help is produced if the arguments are not valid.

History

  • Version1
    • Article created on 5th March of 2010

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader ALGOSYSTEMS
Greece Greece
I live in Athens Greece and currently I am working with Business scale application with .NET latest technologies

I've been developing applications for personal and friends usage with C++ using majorly Borland's various IDEs since 1994.
In 2002 I began working for an R&D institute where I was introduced to C# which I worships ever since.

I love core application development and I would like to publish more articles here and on my blog, but there is not enough time to do so.

I usualy "waste" my spare time watching sitcoms, preferable SCI-FI.
I would like to play chess but I can't find any real world players to hang out with.

Comments and Discussions

 
QuestionWow. Exactly what I needed! Pin
Sharman Staples15-Mar-12 11:52
Sharman Staples15-Mar-12 11:52 
AnswerRe: Wow. Exactly what I needed! Pin
aSarafian16-Mar-12 8:57
aSarafian16-Mar-12 8:57 
GeneralHope ths works Pin
geblack17-Mar-10 2:06
geblack17-Mar-10 2:06 
GeneralRe: Hope ths works Pin
aSarafian17-Mar-10 2:54
aSarafian17-Mar-10 2:54 
GeneralRe: Hope ths works Pin
ahillyer8-Mar-12 10:42
ahillyer8-Mar-12 10:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.