The goal is to get away as little as possible of the standard.
In this case the standard is : Encrypting Configuration Information Using Protected Configuration
In short: use a ProtectedConfigurationProvider. The .Net Framework provides two of them:
- DpapiProtectedConfigurationProvider: uses the Windows Data Protection API (DPAPI) to encrypt and decrypt data.
- RsaProtectedConfigurationProvider: uses the RSA encryption algorithm to encrypt and decrypt data.
The main inconvenience of those providers comes from the fact that they have been designed for web applications: that are applications in which the securisable is the application server. That is one web.config in one machine
With a desktop application you have a plurality of app.config
Said providers are machine based, i.e. there is one configuration file per machine. In other words, the configuration file of one machine can't be used by another because of different encryption keys.
Fortunately the .Net Framework provides an abstract class that can be overridden to built a custom provider.
Here is a sample
The DLL:
using System; using System.Configuration; using System.Xml; namespace MBT.ProtectedConfigurationProviders { public class WeakProtectedConfigurationProvider : ProtectedConfigurationProvider { public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { base.Initialize(name, config); } public override System.Xml.XmlNode Decrypt(System.Xml.XmlNode encryptedNode) { string decryptedData = DecryptString(encryptedNode.InnerText); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.LoadXml(decryptedData); return xmlDoc.DocumentElement; } public override System.Xml.XmlNode Encrypt(System.Xml.XmlNode node) { string encryptedData = EncryptString(node.OuterXml); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.LoadXml("" + encryptedData + " "); return xmlDoc.DocumentElement; } public static String EncryptString(String st) { return Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(st)); } public static String DecryptString(String st) { return WeakProtectedConfigurationProvider.GetString(Convert.FromBase64String(st)); } public static byte[] GetBytes(string str) { byte[] bytes = new byte[str.Length * sizeof(char)]; System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length); return bytes; } public static string GetString(byte[] bytes) { char[] chars = new char[bytes.Length / sizeof(char)]; System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length); return new string(chars); } } }
Here the WEAK word is deliberately chosen. Base 64 encoding is not at all a cypher algorithm.
Then the Program:
using System; using System.Configuration; namespace bas { class Program { static void Main(string[] args) { //String cs = @""; //Console.WriteLine(Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(cs))); Console.WriteLine(ConfigurationManager.ConnectionStrings["SomeName"].ConnectionString); } } }
And finally the app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration > <!-- xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"--> <configProtectedData defaultProvider="WeakProtectedConfigurationProvider"> <providers> <clear /> <add name ="WeakProtectedConfigurationProvider" type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider, WeakProtectedConfigurationProvider"/> </providers> </configProtectedData> <connectionStrings configProtectionProvider="WeakProtectedConfigurationProvider"> <EncryptedData> <CipherData> <CipherValue>PABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAHMAPgA8AGEAZABkACAAbgBhAG0AZQA9ACIAUwBvAG0AZQBOAGEAbQBlACIAIABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAD0AIgBEAGEAdABhACAAUwBvAHUAcgBjAGUAPQBTAG8AbQBlAFMAZQByAHYAZQByADsASQBuAGkAdABpAGEAbAAgAEMAYQB0AGEAbABvAGcAPQBTAG8AbQBlAEQAYQB0AGEAQgBhAHMAZQA7AEkAbgB0AGUAZwByAGEAdABlAGQAIABTAGUAYwB1AHIAaQB0AHkAPQBUAHIAdQBlADsAQwBvAG4AbgBlAGMAdAAgAFQAaQBtAGUAbwB1AHQAPQAxADgAMAA7AE0AdQBsAHQAaQBwAGwAZQBBAGMAdABpAHYAZQBSAGUAcwB1AGwAdABTAGUAdABzAD0AVAByAHUAZQAiACAAcAByAG8AdgBpAGQAZQByAE4AYQBtAGUAPQAiAFMAeQBzAHQAZQBtAC4ARABhAHQAYQAuAFMAcQBsAEMAbABpAGUAbgB0ACIAIAAvAD4APAAvAGMAbwBuAG4AZQBjAHQAaQBvAG4AUwB0AHIAaQBuAGcAcwA+AA==</CipherValue> </CipherData> </EncryptedData> </connectionStrings> </configuration>
At least two points are significant here. First:
<add name ="WeakProtectedConfigurationProvider" type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider, WeakProtectedConfigurationProvider"/>
Here to avoid a GAC registration, on use the syntax "type, assembly"
Second:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
One may want to use a namespace in configuration tag of app.config to avoid Visual Studio complaint. But, in my case this also leads to side effect: "side by side configuration error" which prevents application from starting. So, for now, I do without, and it runs well.
Thank you to StackOverflow for the StringToBytesToString.