After googling a while I couldn't any suitable solution to my problem: I encrypted my app.config, so I get entries like the following in my appSettings section for instance.
<add key="Modules" value="xx+LCaY//dRSq3H5z0TClbQbIUd1fm4krjTelMWu9bwkBB2SybkwMqAchYiLP7ss1EEotHtrZwrVtuc+1la5aVwUebCMMbPUj3reE+1XY1Q=" />
Furthermore I have my connnection strings in format of
<add name="WorkflowEntries" connectionString="NEuhdfCDdGOeGjkk5PvMDl/Zr+75rOSDqifQZE3X6WJ6ZKjqG6J75O8d0b0j1AeXLVMGtkN7oPldtAWqppFKwCliJ+baMA2NVbkpLlMbhc/IH771MWjlC588USC8RzB7lz+BuXas4RS8kkDXmjENDAlEecLYA2nnkMFlXHJxCCOGA35JXTXWHZeQlFU0dBHVJlSUbqTEGTPETqe2tq/WQMfVpRHsLWlrReBplvGYqVZ+T8XTgaJellN0ZJY4f/UV9R2gjOOwvkBUxRJ2djymlbs4nek/oLuTKyCstd5sRluux8V2odplc98ehmVO0KJ0fBFHIzm4qjByj5pke+kc9FsYSTcqQ4KbBbuXwFI1Oc/1wORHOJbZlu40jioAXVDNiCZQh57cm40G9CRJNAE2Ww=="
providerName="System.Data.EntityClient" />
I can build a wrapper around ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings, but EF won't use that. Is there a way to "virtually manipulate" the config, so all parts of the application will access this one?
Hint: I am aware of the power of protected configuration. Since I need to easily deploy my application, this is not an option for me.
I think I got it done. You are welcome to post more eloquent solutions that I think must exist. I created a CustomConfigurationManager where I put all my needed logic into. You can go this way then:
The class is static, so simply calling the CheckConfig() method when your application launches will check for the key "Encrypted" in appSettings. If it already exists nothing happens, elsewise all values in appSettings and the connection strings in the connectionStrings section get encrypted. The key "Encrypted" will be added to the config to prevent the application from encrypting the config again when launching next time.
In a copy-paste action I replaced all occurences of ConfigurationManager.AppSettings by CustomConfigurationManager.AppSettings. When reading from the config (like CustomConfigurationManager.AppSettings["SampleKey"]) the read values are going to be decrypted on the fly.
The most critical part is bringing together the CustomConfigurationManager and Entity Framework. I did this by manually replacing the constructors in the context class. Within this process I made use of the CustomConfigurationManager.ConnectionStrings indexer.
Let's take a closer look on the last step: So with the configuration snippet
<add name="WorkflowEntries" connectionString="NEuhdfCDdGOeGjkk5PvMDl/Zr+75rOSDqifQZE3X6WJ6ZKjqG6J75O8d0b0j1AeXLVMGtkN7oPldtAWqppFKwCliJ+baMA2NVbkpLlMbhc/IH771MWjlC588USC8RzB7lz+BuXas4RS8kkDXmjENDAlEecLYA2nnkMFlXHJxCCOGA35JXTXWHZeQlFU0dBHVJlSUbqTEGTPETqe2tq/WQMfVpRHsLWlrReBplvGYqVZ+T8XTgaJellN0ZJY4f/UV9R2gjOOwvkBUxRJ2djymlbs4nek/oLuTKyCstd5sRluux8V2odplc98ehmVO0KJ0fBFHIzm4qjByj5pke+kc9FsYSTcqQ4KbBbuXwFI1Oc/1wORHOJbZlu40jioAXVDNiCZQh57cm40G9CRJNAE2Ww=="
providerName="System.Data.EntityClient" />
the constructor
public partial class WorkflowEntries : ObjectContext
{
#region Constructors
public WorkflowEntries()
: base("WorkflowEntries", "WorkflowEntries")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
...
becomes
public WorkflowEntries()
: base(CustomConfigurationManager.ConnectionStrings["WorkflowEntries"], "WorkflowEntries")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
If you don't want to manipulate the context classes you can also use the overload that takes the connection string when instantiating the context.
This is my CustomConfigurationManager class. Note, that I removed the encryption and decryption parts, because I think that those are not relevant in the context of this question.
public static class CustomConfigurationManager
{
private static string pwd = "ThisIsNotBestPracticeForStoringPasswords".Select(x => x.ToString() + (x + 2).ToString()).Aggregate((current, next) => current + next);
public static AppSettingsIndexer AppSettings = new AppSettingsIndexer(pwd);
public static ConnectionStringsIndexer ConnectionStrings = new ConnectionStringsIndexer(pwd);
public static void CheckConfig()
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var appSettings = config.AppSettings;
var connectionStrings = config.ConnectionStrings.ConnectionStrings;
if (!appSettings.Settings.AllKeys.Contains("Encrypted"))
{
EncryptConfig(pwd, config, appSettings, connectionStrings);
}
}
private static void EncryptConfig(string pwd, Configuration config, AppSettingsSection appSettings, ConnectionStringSettingsCollection connectionStrings)
{
foreach (var key in appSettings.Settings.AllKeys)
{
appSettings.Settings[key].Value = StringCipher.Encrypt(appSettings.Settings[key].Value, pwd);
}
for (int i = 0; i < connectionStrings.Count; i++)
{
connectionStrings[i] = new ConnectionStringSettings(connectionStrings[i].Name, StringCipher.Encrypt(connectionStrings[i].ConnectionString, pwd), connectionStrings[i].ProviderName);
}
appSettings.Settings.Add("Encrypted", "True");
config.Save(ConfigurationSaveMode.Modified, true);
}
static class StringCipher
{
internal static string Encrypt(string plainText, string passPhrase)
{
if (plainText.Trim() == "")
return "";
/* Add your encryption stuff here */
}
internal static string Decrypt(string cipherText, string passPhrase)
{
if (cipherText.Trim() == "")
return "";
/* Add decryption stuff here */
}
}
public class AppSettingsIndexer
{
static string pwd;
public AppSettingsIndexer(string _pwd)
{
pwd = _pwd;
}
public string this[string index]
{
get
{
return StringCipher.Decrypt(ConfigurationManager.AppSettings[index], pwd);
}
}
}
public class ConnectionStringsIndexer
{
static string pwd;
public ConnectionStringsIndexer(string _pwd)
{
pwd = _pwd;
}
public string this[string index]
{
get
{
var connectionString = ConfigurationManager.ConnectionStrings[index];
return new ConnectionStringSettings(connectionString.Name, StringCipher.Decrypt(connectionString.ConnectionString, pwd), connectionString.ProviderName).ToString();
}
}
}
}