Tuesday, September 29, 2009

Encrypting connection strings in .NET

There is a lot of documentation on the web about this (my favorites are at the bottom of this post) but I really wanted a summary of how to encrypt strings using my own key that could be moved from server to server, and how to access those strings from a web app / code-behind. So this is what I came up with. I'm posting it so I can return to it later as I learn more; take it with a grain of salt.



Generating an exportable / movable RSA key and encrypting using your custom key




  1. Generate an RSA Key container

    aspnet_regiis -pc "SampleKeys" –exp


  2. Tie the RSA key container to your web.config (<configProtectedData> is a top-level key, like connectionStrings or appSettings) - note that this should be identical to the config for RsaProtectedConfigurationProvider in your machine.config, with a different value for the defaultProvider, name and keyContainerName attributes.

    <configProtectedData defaultProvider="SampleProvider">
    <providers>
    <add name="SampleProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="SampleKeys" cspProviderName="" useMachineContainer="true" useOAEP="false"/>
    </providers>
    </configProtectedData>


  3. Grant access to the key container to the user your process runs as

    aspnet_regiis -pa "SampleKeys" "ASPNET"


    1. This sets up permissions for the "default" RsaProtectedConfigurationProvider. I had to set this up for the ASPNET user along with permissions for my own container above.

      aspnet_regiis -pa "NetFrameworkConfigurationKey" "ASPNET"

    2. If the user your process is running as doesn't have permission to use the provider, you'll get an error "Failed to decrypt using provider 'RsaProtectedConfigurationProvider'."

    3. I recently ran into a server that didn't have the "NetFrameworkConfigurationKey" - I think this is part of the normal .NET Framework setup so I don't know why it might have been missing. Generating the key fixed the problem:

      aspnet_regiis -pc "NetFrameworkConfigurationKey"




  4. Encrypting strings - if you're not using the default site you'll need the site identifier (shown in IIS manager window under "Web Sites"?)

    aspnet_regiis -pe "connectionStrings" -site 1 -app "/SampleApplication" -prov "SampleProvider"


  5. Other Useful Commands

    1. Decrypting strings (in case you need to change something)

      aspnet_regiis -pd "connectionStrings" -app "/SampleApplication"


    2. Export the public and private keys for import on another machine

      aspnet_regiis -px "SampleKeys" keys.xml -pri


    3. Importing the keys

      aspnet_regiis -pi "SampleKeys" keys.xml


    4. Removing access to a no longer needed key

      aspnet_regiis -pr "SampleKeys" "ASPNET"


    5. Deleting a no longer needed key container

      aspnet_regiis -pz "SampleKeys"





Using encrypted strings in code


Most of the canned demo code on the internet shows you opening, testing for the strings to be encrypted, decrypting them, then saving the config file back. I don't think I want to save the unencrypted strings, as this would seem to defeat the purpose of encrypting them in the first place. I may be misunderstanding the code samples, but this is what I came up with and it makes more sense to me.

static string GetProtectedConnectionString(string name)
{
// Open the Web.config file.
Configuration config = WebConfigurationManager.
OpenWebConfiguration("~");

// Get the connectionStrings section.
ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;

// Toggle encryption.
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
ConnectionStringSettings s = section.ConnectionStrings[name];
return s.ConnectionString;
}
else
{
throw new ConfigurationException("Warning: connection strings are not encrypted");
}
}


This code probably needs a bit more cleanup. The point is, you don't save the unprotected section, but you decrypt the section long enough to get the string from the config file that you were looking for. Note that you could easily modify this to just return the connection string whether or not it was encrypted but I decided I'd rather be notified if the config ends up unprotected for some reason. Which brings up a valuable point: when you re-publish your site, you will need to re-encrypt your strings! Add this as a post-build step!


Useful links


Securing Connection Strings
http://msdn.microsoft.com/en-us/library/89211k9b(VS.80).aspx


Encrypting Configuration Information Using Protected Configuration
http://msdn.microsoft.com/en-us/library/53tyfkaw(VS.80).aspx


Importing and Exporting Protected Configuration RSA Key Containers
http://msdn.microsoft.com/en-us/library/yxw286t2(VS.80).aspx


Walkthrough of encrypting connection strings and accessing them from ASPx
http://msdn.microsoft.com/en-us/library/dtkwfdky(VS.80).aspx


How To: Encrypt with ASP.NET 2.0 using RSA
http://msdn.microsoft.com/en-us/library/ms998283.aspx