Introduction
It all happened when I came across a need to encrypt sensitive information in my Windows Forms application’s configuration file. I decided to use the new Protected Configuration feature of Microsoft .NET 2.0 for encrypting it.
I started searching the web but was unable to find any practical application of Protected Configuration for Windows Forms applications. Almost all of the online resources tell you to do that with ASP.NET. In most of the articles, encryption is done via the aspnet_regiis tool, and aspnet_regiis does not support encryption of Windows Forms application configuration files, so virtually one is left with no option.
Maybe I am not completely correct in saying this; few resources on the web do mention that you can use the System.Configuration.SectionInformation.ProtectSection
method to encrypt a Windows Forms application configuration file as well. But none gives an example to use in practical desktop scenarios.
When it comes to a real life implementation of the ProtectSection
method, there are the following problems:
- The
ProtectSection
method is not a command line tool like apnet_regiis, one has to call it at runtime from the application’s code. By using this method, a particular configuration section of the application configuration file can be encrypted. So probably, using code such as the following in the application’s Main
method will work out.
Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
ConfigurationSection section =
config.GetSection("connectionStrings");
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection(
"MyUserDataProtectionConfigurationProvider");
}
section.SectionInformation.ForceSave =true;
config.Save(ConfigurationSaveMode.Modified);
Here, the IsProtected
property of System.Configuration.SectionInformation
determines whether the configuration section is already encrypted.
But there is a problem, the Main
method will be called when the application is run. So all the sensitive information will lie naked in the config file until and unless a user runs the application for the first time.
- Even if you manage to encrypt the configuration section via making some stand-alone application to do this, there are again two problems in the approach:
- None of the built in
ProtectedConfigurationProviders
, i.e., RsaProtectedConfigurationProvider
and DpapiProtectedConfigurationProvider
, are very well suited to such a scenario because, encryption done from one machine cannot always be decrypted on another one.
- This one is interesting. The actual file encrypted will be Exe_Name.exe.config, not the app.config itself. And as we know, by default, Exe_Name.exe.config is overwritten each time we build our project.
To add a little more to the annoyance, while debugging, one wouldn’t even notice the change in the Exe_Name.exe.config file if the Visual Studio Hosting Process in enabled (which is enabled by default). If so, Exe_Name.vshost.exe.config will be encrypted instead. So all this will make the process of porting the encrypted version of the configuration file from production to deployment a headache.
As you may have realized by now, the Protected Configuration is not a fancy option with Windows applications.
As the ProtectSection
method is our only way out for Windows application configuration files, we need to determine a way to use the ProtectSection
method in the following manner:
- Even before the application is first run
- Only once
- On the user's machine
- On the right configuration file
There is a very simple solution for all the problems mentioned above. Use the System.Configuration.SectionInformation.ProtectSection
method in an installer class. An MSI setup of your application can call this installer class. So, the configuration information will be encrypted at the time of installation on the user’s machine. Then, .NET takes care of the decryption at runtime.
Background
For those of you who are not familiar with the new Protected Configuration feature of .NET 2.0, it enables you to encrypt application configuration information and configure the application to automatically decrypt it at runtime. This is implemented by using different Protected Configuration Providers, which can be configured in App.config files. Each provider has its own method of performing the encryption. For details, take a look at the MSDN article: Encrypting Configuration Information Using Protected Configuration.
Using the Code
Among the two built-in Protected Configuration Providers, i.e., DpapiProtectedConfigurationProvider
and RSAProtectedConfigurationProvider
, I shall make use of DpapiProtectedConfigurationProvider
, because in my opinion, it is more suited for Windows apps. It uses the Windows built-in cryptographic services, and can be configured for either machine-specific or user-account-specific protection.
Note: The purpose of this article is not to demonstrate the best possible encryption mechanism, but to demonstrate how Protected Configuration can be used with Windows Forms apps.
Feel free to implement your own provider, which may use a custom algorithm. See Implementing a Protected Configuration Provider for more details.
Recreating the Demo Project
To recreate the demo project, follow the steps mentioned below.
Creating the Windows Application Project
Open Visual Studio 2005, and create a new C# Windows Application project. Name it DemoWinApp.
Open the Settings file, and add a setting named SecretMessage
as shown below:
Open Form1
’s designer, and add a Label
control on it, naming it lblSecretMessage
as shown below:
Add the following code in Form1
’s Load
method.
lblSecretMessage.Text = DemoWinApp.Properties.Settings.Default.SecretMessage;
Specifying the Protected Configuration Provider
Open the App.Config file, and add the following code as the child node of the Configuration
section.
<configProtectedData>
<providers>
<add useMachineProtection="true" name="DPAPIProtection"
type="System.Configuration.DpapiProtectedConfigurationProvider,
System.Configuration, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</configProtectedData>
The above code is necessary to configure a Protected Configuration provider, which will be used for encrypting and decrypting the configuration section which contains sensitive information. Whenever you want to specify your own provider with custom settings, you can declare a new provider instance using the add
element of the providers
element under ConfigProtectedData
, which in turn is a child element of the Configuration
section.
The DpapiProtectedConfigurationProvider
uses the Windows built-in cryptographic services, and can be configured for either machine-specific or user-account-specific protection. Machine-specific protection is useful for anonymous services, but provides less security. User-account-specific protection can be used with services that run with a specific user identity.
Notice the attribute useMachineProtection=true
in the above listing. This configures the provider to use machine specific protection. So, any user can successfully use the application on that machine. Making its value false
will configure the provider to use user-account-specific protection. Here, using the application means, the .NET runtime will not be able to decrypt the configuration information encrypted earlier. As, all Windows applications, by default, run under the currently logged in user accounts and we plan to encrypt the configuration information during installation. So, if we use user-account-specific protection, only the user who will install the application will be able to use the application.
For this article, we shall use the machine specific protection.
The attribute name="DPAPIProtection"
will be used to encrypt the configuration section during the installation process.
Other attributes like type
, Version
, Culture
, and PublicKeyToken
identify the DpapiProtectedConfigurationProvider
assembly installed in the Global Assembly Cache by default.
Here is how the app.config file looks like:
="1.0"="utf-8"
<configuration>
<configSections>
<sectionGroup name="applicationSettings"
type="System.Configuration.ApplicationSettingsGroup,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" >
<section name="DemoWinApp.Properties.Settings"
type="System.Configuration.ClientSettingsSection,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</sectionGroup>
</configSections>
<applicationSettings>
<DemoWinApp.Properties.Settings>
<setting name="SecretMessage" serializeAs="String">
<value>This is the secret message.</value>
</setting>
</DemoWinApp.Properties.Settings>
</applicationSettings>
<configProtectedData>
<providers>
<add useMachineProtection="true" name="DPAPIProtection"
type="System.Configuration.DpapiProtectedConfigurationProvider,
System.Configuration, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</configProtectedData>
</configuration>
Adding an Installer Class
As mentioned earlier, we shall use the ProtectSection
method of the System.Configuration.SectionInformation
class in the installer class in order to encrypt sensitive configuration information at the time of installation.
Add a new installer class in your project, and name it DemoWinAppInstaller.cs.
For those of you who are not familiar with using installers, installers are components that help install applications on a computer. A custom class is derived from the base Installer
class, and any or all of the Install
, Commit
, Rollback
, and Uninstall
methods can be overridden in the custom class. From the Visual Studio setup projects, these methods are configured to be called at various stages of the installation process. As you should have guessed, these stages are Install, Commit, Rollback, and Uninstall.
Here is the code listing of the custom installer class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Configuration;
namespace DemoWinApp
{
[RunInstaller(true)]
public partial class DemoWinAppInstaller : Installer
{
public DemoWinAppInstaller()
{
InitializeComponent();
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string sectionName = this.Context.Parameters["sectionName"];
string provName = this.Context.Parameters["provName"];
string exeFilePath = this.Context.Parameters["assemblypath"];
ProtectSection(sectionName, provName, exeFilePath);
}
private void ProtectSection(string sectionName,
string provName, string exeFilePath)
{
Configuration config =
ConfigurationManager.OpenExeConfiguration(exeFilePath);
ConfigurationSection section = config.GetSection(sectionName);
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection(provName);
}
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
}
}
}
Notice that only the Install
method has been overridden in the custom installer, and this contains the code for encrypting the Configuration
section. The Install
method retrieves the parameters, i.e., sectionName
(configuration section to be encrypted) and provName
(provider name as defined in the configuration file, which will be used to encrypt the configuration section) passed to the installer (not the Install
method). We shall see how the parameters are passed to an installer, in a moment.
Also notice that, you don’t have to pass the assemblypath
parameter; it is present in the Installer
’s Context
, by default.
The Install
method then calls a private method, ProtectSection
, in order to encrypt the specified configuration section. This method will encrypt the configuration section using the actual System.Configuration.SectionInformation.ProtectSection
method and saves the changes to the configuration file.
The OpenExeConfiguration
method of the System.Configuration.ConfigurationManager
class requires an EXE path to be passed to it, and it finds the configuration file of the specified EXE itself.
The ProtectSection
method of the System.Configuration.SectionInformation
class needs the provider name, which will be used to encrypt the configuration section.
The DemoWinApp application is complete. Don’t forget to build the project.
Creating the Visual Studio Setup Project
Now, let's see how a Visual Studio Setup project can be created, which will make use of the installer class presented above to encrypt the sensitive configuration information upon installation.
Add a new setup project to the solution, and name it DemoWinAppSetup.
From the File System Editor, add the primary output of the DemoWinApp project to the setup project.
The key to using an installer class with a Visual Studio Setup project is Custom Actions.
Open the Custom Action Editor, and add the primary output of the DemoWinApp project under the Install folder.
Open the Properties window of the newly added custom action, and add custom action parameters and their values in the CustomActionData
property.
Notice the format of specifying the parameters. Each parameter takes the form /[Parameter Name]="[Value]". Individual parameters are delimited by spaces. Make sure there are no spaces between the parameter name, equal sign, and the parameter value.
To encrypt the whole Settings
section, the sectionName
parameter has been provided the value applicationSettings/DemoWinApp.Properties.Settings
, which is the path of the Settings
node in the App.Config file.
Note that adding a value in the Settings.settings file automatically creates the Settings
node in the App.Config, as shown below.
<applicationSettings>
<DemoWinApp.Properties.Settings>
<setting name="SecretMessage" serializeAs="String">
<value>This is the secret message.</value>
</setting>
</DemoWinApp.Properties.Settings>
</applicationSettings>
The provName
is the provider name as specified in the App.config file earlier.
Now we are ready to go. Build the Setup project to generate the MSI Installer package.
Running the Application
Run the generated installer package to install the application. Go to the installation folder, and open the configuration file. Note the applicationSettings/DemoWinApp.Properties.Settings
section.
In my case, it looks like the following:
<applicationSettings>
<DemoWinApp.Properties.Settings configProtectionProvider="DPAPIProtection">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAJY9eXbg340Gn
Ay8Dyzkf4QQAAAACAAAAAAADZgAAqAAAABAAAAAac+j1
UbifDAtrOUt9xNUWAAAAAASAAACgAAAAEAAAAKwqPS6A
bcFdlDZiS8gBMvCIAQAAgBPMemCdMCcX6juvCaB02kNJ
Vb3tyHHLztYWkMyNgs60cIBiqQbNYPUcPkuMg42tbffk
0VQ0KEwMu8/mRAc4+LQwni9kRbpSR4GOUPoOzLSrdxcK
6D1mjntJ+804dZ7fk9gq793GpPJroV0VfxoaMBDZtHKw
uRbAnFvVn+yWfH6ZVN1gQFIM3vhinc/kpiBB+pPLCO/5
XbsOfwu3eLSw436LGqfTPsvAj6JY6pax8hd7KnIsCDte
EkZVGjHAfJnSj6mB5vI9u7fBLwjTa0V4qhznW+lcb6uP
JeR565sRnJq7Od+3c716bJ6fOS/AY91zF+f5rYLN+ebZ
ZnKabrilA6+xS70+rSPuLnXueKp6UymP0R4k9oVjfmAm
Utd4/PYuZqk+nKbEyJwr8lzc7lkwsy/aYE7IK9/BHlDf
rpJfR0B11ZeUBmXbGLD0N0hFQHZO6FHDxnRIzadJx7UX
o5VGVoE63tjAnDfRtj/UbudIq8GhM8CHxkh0o/AUuEpo
PsobGQ576EDdwo4UAAAAP2wB/QutHyUIYCG6T7n6YNbE
4gg=</CipherValue>
</CipherData>
</EncryptedData>
</DemoWinApp.Properties.Settings>
</applicationSettings>
So as you can see, the installation process has encrypted the SecretMessage
setting, which was the child node of the applicationSettings/DemoWinApp.Properties.Settings
section.
Note that the configProtectionProvider
attribute has been automatically added to the DemoWinApp.Properties.Settings
section, which tells the .NET runtime that the configuration section is encrypted and which provider to use for decryption.
Now, the responsibility of decrypting the configuration section is on the .NET runtime, not the application programmer.
To recall, in the DemoWinApp application, we did not add any code for decrypting the SecretMessage
setting before accessing it.
lblSecretMessage.Text = DemoWinApp.Properties.Settings.Default.SecretMessage;
Now, run the DemWinApp application installed on your PC.
The output should look like the following:
Conclusion
The ProtectedConfiguration
API is a nice and clean way to protect sensitive information in the application’s configuration file. Unfortunately, the built-in ProtectedConfigurationProvider
s are not very well suited for many Windows application scenarios, e.g., an advance user can easily develop his own application and decrypt the configuration information for the provider configuration (machine specific DpapiProtectedConfigurationProvider
) used in this article. But still, for many scenarios, one can use it neatly with Windows applications as well. As I mentioned earlier, the purpose of this article is not to demonstrate the best possible encryption mechanism, but to demonstrate how Protected Configuration can be used with Windows Forms apps. If you want more control over the actual encryption algorithm, you can easily make a custom ProtectedConfigurationProvider
. See Implementing a Protected Configuration Provider, for more details.