Thursday, September 20, 2007

Secured section inside web.config

Finally a chance to work on some code - six months of writing documentation just isn't good for the health!!

So in my web app I want to store an ID and password, and I want to encrypt these keys to secure them. But there are other settings I want to leave in clear text to make for easier editing for my admins, and less web page work for me. The answer - custom sections inside my ASP.Net 2.0 web.config.

Good stuff! But as usual, it ain't straight forward. Here's what I ended up implementing. The web.config contains the following entry to create the new secured section:

















Then inside my project I created the following class. It sets up the key/value pairs I needed inside my secure settings section of web.config.

Then, to use the class, easy peasy - just need to iterate through the pairs to find the key I need:

private string GetSecureConfigKey(string key)
{
String retval = "";
SecureConfiguration configInfo = (SecureConfiguration)ConfigurationManager.GetSection("secureSettings");
if (configInfo != null)
{
foreach (SecureConfigurationADKey secureKey in SecureConfiguration.GetConfig().ADKeys)
{
if (secureKey.Key.ToLower().Equals(key.ToLower()))
retval = secureKey.Value;
}
}
return retval;
}

To set the value, I needed to do something similar - but kept getting an error message that the configuration was read only. Huh?? Problem was I tried setting the value inside my foreach loop - tsk tsk. So instead I use the following:

Configuration objConfig = WebConfigurationManager.OpenWebConfiguration("~");
SecureConfiguration configInfo = objConfig.GetSection("secureSettings") as SecureConfiguration;
foreach (SecureConfigurationADKey secureKey in SecureConfiguration.GetConfig().ADKeys)
{
if (secureKey.Key.ToLower().Equals(Key.ToLower()))
{
index = i;
}
i++;
}

then

configInfo.ADKeys[index].Value = Value;

And all's well that ends well. Note too that I check the .IsProtected property of my section when I save the value:

if (!configInfo.SectionInformation.IsProtected)
{
configInfo.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
configInfo.SectionInformation.ForceSave = true;
}

and it works!

The class I use for the new web.config section follows:
public class SecureConfiguration : ConfigurationSection
{
public static SecureConfiguration GetConfig()
{
return ConfigurationManager.GetSection("secureSettings") as SecureConfiguration;
}

[ConfigurationProperty("adInfo")]
public SecureConfigurationADKeysCollection ADKeys
{
get
{
return this["adInfo"] as SecureConfigurationADKeysCollection;
}
set
{
this["adInfo"] = value;
}
}
}

public class SecureConfigurationADKey : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get
{
return this["key"] as string;
}
set
{
this["key"] = value;
}
}

[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get
{
return this["value"] as string;
}

set
{
this["value"] = value;
}
}
}

public class SecureConfigurationADKeysCollection : ConfigurationElementCollection
{
public SecureConfigurationADKey this[int index]
{
get
{
return base.BaseGet(index) as SecureConfigurationADKey;
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}

protected override ConfigurationElement CreateNewElement()
{
return new SecureConfigurationADKey();
}

protected override object GetElementKey(ConfigurationElement element)
{
return ((SecureConfigurationADKey)element).Key;
}
}