Encrypt sections of Web.Config or App.Config

March 3, 2008 18:03 by garrymc

In many application be they web or otherwise it can be important to secure parts of the configuration files incase the file is compromised by a hacker or you simply don't want anyone to know what the true values are. It turns out that this is fairly easy to do using the ASP.NET IIS Registration Tool (Aspnet_regiis.exe) (obvious right?). To illustrate the point I'll show you a typical config file and what's required to encrypt parts of it.

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Initial Config file without taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.
   9:             Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data,
  10:             Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  11:   </configSections>
  12:   <dataConfiguration defaultDatabase="MyDatabase">
  13:     <providerMappings>
  14:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  15:             Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  16:             PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  17:     </providerMappings>
  18:   </dataConfiguration>
  19:  
  20:   <appSettings>
  21:     <add key="BufferSize" value="999999"/>
  22:     <add key="SomeService.UserName" value="secure-username"/>
  23:     <add key="SomeService.Password" value="secure-password"/>
  24:   </appSettings>
  25:   
  26:   <connectionStrings>
  27:     <add name="MyConnectionString" connectionString="Data Source=.;
  28:         Integrated Security=SSPI;Initial Catalog=MyApplicationDB;" 
  29:         providerName="System.Data.SqlClient" />
  30:   </connectionStrings>
  31: </configuration>

The settings of each of these can easily be retrieved using the following code which is included in the download as a console app.

   1: static void Main(string[] args)
   2: {
   3:     // Write out the contents of the config file.
   4:     Console.WriteLine("MyConnectionString = " + 
   5:         ConfigurationManager.ConnectionStrings["MyConnectionString"]);
   6:     Console.WriteLine("BufferSize = " + ConfigurationManager.AppSettings["BufferSize"]);
   7:     Console.WriteLine("SomeService.UserName = " + 
   8:         ConfigurationManager.AppSettings["SomeService.UserName"]);
   9:     Console.WriteLine("SomeService.Password = " + 
  10:         ConfigurationManager.AppSettings["SomeService.Password"]);
  11:  
  12:     Console.ReadLine();
  13: }

In the example above you may want to encrypt the connection string and two of the keys in appSettings, but probably not the BufferSize property as you'd like that to be easily accessible or at least readable on the production server. The problem we have is that the Aspnet_regiis.exe tool only works on entire sections ie appSettings, connectionStrings. So encrypting the connectionStrings section is fairly straightforward and can be done with the following command lines:

   1: copy App.Config App.Config.original
   2: rename App.config web.config
   3: aspnet_regiis -pef connectionStrings . -prov DataProtectionConfigurationProvider
   4: rename web.config App.config

So this is a little strange and the reason is that the tool only works with web.config files, however by renaming, then processing our app.config file we can get the desired result. I've included this as a batch file in the project download. You just need to run the batch file in the directory that your app.config file resides in. If you want to run this against your web.config then you only need the third line. The new config file should look something like this (I've reformatted it a bit):

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Initial Config file without taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.
   9:         Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data,
  10:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  11:   </configSections>
  12:   <dataConfiguration defaultDatabase="MyDatabase">
  13:     <providerMappings>
  14:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase,
  15:          Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  16:          PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  17:     </providerMappings>
  18:   </dataConfiguration>
  19:  
  20:   <appSettings>
  21:     <add key="BufferSize" value="999999"/>
  22:     <add key="SomeService.UserName" value="secure-username"/>
  23:     <add key="SomeService.Password" value="secure-password"/>
  24:   </appSettings>
  25:   
  26:   <connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
  27:     <EncryptedData>
  28:       <CipherData>
  29:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAA
  30:         AAAADZgAAqAAAABAAAAAd1TPFE24MHo78J8HYFRXKAAAAAASAAACgAAAAEAAAAAJ8yjPvsnqP0Lo
  31:         Qt05ynrWoAQAANJdt/En21VeyYb8OJcTE3p+MeKOMG4xsFPzjlrHnyaJmVEekYVMCkX2RG/1pV1J
  32:         DfsaCAXKSyBnxDiCM5yAQEA5TSxeedQYvKXWBWrUxMMIjNUmjZDsbc8I/eBKarMCHav7d+oguijn
  33:         HFf948x48Z25434QY1lZ4hnkP7oW5dO9639iaT3+uWJRoARzHzcfvNYxtJ/+jowoZyt+JRcHjR1Q
  34:         MBD/CMsehNh4nQeWX2zp09tYm2EdIj7LmSyWwrE9Livf1qs/IBGRv1zWPIue956mAwDbXPg4tuF1
  35:         Ml4Qdm3rj9+mCp/sht80ixkWM9nID8/OaqZqzaaK/J/3vMjZF+w4SeFws8Vo40xb48/Mr1Q5EOZe
  36:         JMNa1ySiard4pckR1Xd3rRPA+J1+6BBTCws0qsmBtd5pVs0KW0xt0HIlUELA7NYIsXML+zGFgJ2C
  37:         TTGcTQsUF5y3+soUeXxfM2Eq3NqSVouWJMImT2udvR6PCiHGsNMiT9/ZK8TL0C5aAzD47IAc0ngA
  38:         DF/uTcX8ERWUy7LPwIlDRXamTlRmVvXt712na83nFf+FExhQAAADDz+jCgtqU0npcs6WJgVP/+QU
  39:         cQQ==</CipherValue>
  40:       </CipherData>
  41:     </EncryptedData>
  42:   </connectionStrings>
  43: </configuration>

So that's how easy it is to encrypt a section of the config file, however, we still have an issue with encrypting part of our appSettings. The problem is you can't selectively encrypt part of a section, its an all or nothing scenario. Therefore, to achieve our goal, we have to add a new section and encrypt that. This leaves us with a config file that looks like this:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Config file taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
   9:         System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  10:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.
  11:         Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, 
  12:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  13:   </configSections>
  14:   <dataConfiguration defaultDatabase="MyDatabase">
  15:     <providerMappings>
  16:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  17:         Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral, 
  18:         PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  19:     </providerMappings>
  20:   </dataConfiguration>
  21:  
  22:   <appSettings>
  23:     <add key="BufferSize" value="999999"/>
  24:   </appSettings>
  25:  
  26:   <secureAppSettings>
  27:     <add key="SomeService.UserName" value="secure-username"/>
  28:     <add key="SomeService.Password" value="secure-password"/>
  29:   </secureAppSettings>
  30:  
  31:   <connectionStrings>
  32:     <add name="MyConnectionString" connectionString="Data Source=.;Integrated Security=SSPI;
  33:         Initial Catalog=MyApplicationDB;" providerName="System.Data.SqlClient" />
  34:   </connectionStrings>
  35: </configuration>

The new parts have been highlighted and show that I've added a new secureAppSettings (can be anything) section and then added the section with the name value pairs that I want to encrypt. Now all I need do is modify the original batch file to include this section too:

   1: copy App.Config App.Config.original
   2: rename App.config web.config
   3: aspnet_regiis -pef connectionStrings . -prov DataProtectionConfigurationProvider
   4: aspnet_regiis -pef secureAppSettings . -prov DataProtectionConfigurationProvider
   5: rename web.config App.config

This then produces the following encrypted config file ready for deployment - well almost...

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Config file taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
   9:         System, Version=1.0.3300.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  10:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.
  11:         Configuration.DatabaseSettings,Microsoft.Practices.EnterpriseLibrary.Data, 
  12:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  13:   </configSections>
  14:   <dataConfiguration defaultDatabase="MyDatabase">
  15:     <providerMappings>
  16:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  17:         Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  18:         PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  19:     </providerMappings>
  20:   </dataConfiguration>
  21:  
  22:   <appSettings>
  23:     <add key="BufferSize" value="999999"/>
  24:   </appSettings>
  25:  
  26:   <secureAppSettings configProtectionProvider="DataProtectionConfigurationProvider">
  27:     <EncryptedData>
  28:       <CipherData>
  29:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAAA
  30:         AAADZgAAqAAAABAAAACF8idXnx0O07526CxQFh9BAAAAAASAAACgAAAAEAAAACNxLiQ6eAyzE8jZG
  31:         yUhwThYAQAAii89rtpXJsxeklxD/7LhjtLzzaRGxcCpWUDuI+uNKuN0GrDeKSXRB0s7tfumGdzkZM
  32:         iR6qlTE9ueIDWO8tm79pVJ3emkYSiBRGi4KXs5oAPugwyuZ/Qfq3LC2d1U07EWGvOBQK4SjTC56De
  33:         KKZDLwu+3WRBYLGLdJapCnMQ4lB/Ev29R9XYW9iqQfgOFE3iFdtcfts4+8Ig7y6CYe3A845VZWBD0
  34:         6SFu2obFZ8vGCRmF2qr0M9N9uxT+xOCRX7G5vkbH+TpFSiUQoV+cC5f9y98uCyj2xL60GxO+PZM8W
  35:         UJFYknRoX6SkVzfRwX+GWcdRc+nYSM1+I/hica32POuWur5lAhye0JO2SHTKSj66S+XjiM64fkh+b
  36:         HNp1LXf0ciEZQNIZeknVzClns6vHHDYYPI3iPYs+dTv3hoNPSDe4kee9BhuPWgRBS3uObPt6ySv46
  37:         QHJH4QWsUAAAAxcEHWBLJ1zw1Y5etR7t4EFeU5X8=</CipherValue>
  38:       </CipherData>
  39:     </EncryptedData>
  40:   </secureAppSettings>
  41:  
  42:   <connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
  43:     <EncryptedData>
  44:       <CipherData>
  45:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAAA
  46:         AAADZgAAqAAAABAAAABv17Dh+HaRnrZppOfniJMaAAAAAASAAACgAAAAEAAAAF1mqOQdo+lvm45mQ
  47:         MwaiaKoAQAAHVHExOIDaItnnbRK7n/qHudiSUAq22t2qRTSxQDxE/lYyN2gA/cWHTw4njHVRu+MJN
  48:         awquuIFKW7RLFAlDYdJPA2xPHakwJXt6MLCOgdXmd+YojqP7AYHrPbFuRGF/8sNY+SlH0QOZya53D
  49:         AuljzMUQoE27BQbsCT+tLriMnGgmc8SaCjAFqqVc538zxxKLFnLZJLttHm5qAq7/y65abZDMjYo4Y
  50:         m4RpmhxQ5ZyITWpGt8RWabZ10MgWvcWS+QiL+ONVhs7lgR5wo0tDk1aoMh0f2VrSZPfXYoC8ZR2JV
  51:         0jqVSNasYmZN/+AO2G+6/S8YxETJqYD3BL8qFBtok9NUzvvoGMZYw+lJniSoLCBWtNoFpFM90/v0x
  52:         hCfRZZqiYj/8OOpJQT38ycG1N/zskhK/LAKe7loZGgPMl1wUjnFGpLOHW+f2ikVtYtHkH3+qxqny8
  53:         AeSZKovMnhfZlXUJx18G0Ugpvskmgju9aKtnE6UWLZbiiygJAfifQUjTwjFXgibQmuvFPxlWuUSW7
  54:         e6fBzVggx1zpDqnu5RGksN+zOg3S7IyiC79GihQAAAAWBM1NaI+3SdfalbKCGvuhxxTNSQ==
  55:         </CipherValue>
  56:       </CipherData>
  57:     </EncryptedData>
  58:   </connectionStrings>
  59: </configuration>

If we try and run our original console app the values for our secure settings won't be retrieved, which if you've been paying attention you'll no-doubt have worked this out as the values are no longer part of the appSettings section but part of the secureAppSettings section. So we need to update our original console app to look at this section instead:

   1: static void Main(string[] args)
   2: {
   3:     // Write out the contents of the config file.
   4:     Console.WriteLine("MyConnectionString = " + 
   5:         ConfigurationManager.ConnectionStrings["MyConnectionString"]);
   6:     Console.WriteLine("BufferSize = " + ConfigurationManager.AppSettings["BufferSize"]);
   7:  
   8:     // Access the secureAppSettings
   9:     NameValueCollection secureAppSettings = 
  10:         (NameValueCollection)ConfigurationManager.GetSection("secureAppSettings");
  11:     Console.WriteLine("SomeService.UserName = " + secureAppSettings["SomeService.UserName"]);
  12:     Console.WriteLine("SomeService.Password = " + secureAppSettings["SomeService.Password"]);
  13:  
  14:     Console.ReadLine();
  15: }

Running this version gives the same result as the original except that now all the parts that we want encrypted are. The really cool thing is that this works if the config is encrypted or not, which allows you to work with an un-encrypted version during dev and then have your Ops people encrypt it (once they put the real values in) just prior to deploy or as part of the deployment script.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList
kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Related posts

Comments

March 31. 2008 15:00

Jay

Excellent post mate. Very handy to know.

No need of writing a bespoke tool using .NET to do this.

Jay

March 31. 2008 17:29

garrymc

Thanks of the comment... I should point out that you do need to run the batc file or the Aspnet_regiis.exe commands on the server you deploy to. This is because it uses a machine key for the encryption. It is possible to use a user key too, but it gets a bit complicated.

garrymc

April 1. 2008 18:44

Pablo

Thanks, great help!!

Just to complement with a piece of help,

For instantiating NameValueCollection the following namespace must be imported:

Imports System.Collections.Specialized

Pablo

May 27. 2008 04:50

Corne

I am using asp.net 3.5 (visual studio 2008). When I add the new section in my webconfig file, I am getting an error "Unrecognized configuration section secureAppSettings."

Is this possible with a web.config as well?

Corne

June 3. 2008 14:31

GarryMc

I've not tried it out myself but it should be the same, ensure that you've included lines 8 & 9 from the sedond to last listing (ie section name="secureAppSettings" etc). This tells ASP.NET about your new config entry.

GarryMc

June 4. 2008 01:17

Corne

Thank you Garry, tested it with web.config and is working 100%

Regards
Corne

Corne

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

July 5. 2008 08:33