Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I encrypt app.config file sections during install with WiX?

I have found an example for encrypting a web.config during installation here, but my app is a windows service. The aspnetreg_iis method works only for web.config files.

I know how to programatically encrypt the config file, but I don't think I can do that using WiX. Am I wrong? Any ideas?

like image 352
JasonS Avatar asked Nov 20 '08 21:11

JasonS


2 Answers

Here is what I ended up with...

I used WiX and DTF to created a managed code Custom Action to encrypt a given section of a config file:

    public static void EncryptConfig(Session session)
    {

        var configPath = session["APPCONFIGPATH"];
        var sectionToEncrypt = session["SECTIONTOENCRYPT"];

        var fileMap = new ExeConfigurationFileMap();
        fileMap.ExeConfigFilename = configPath;
        var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        ConfigurationSection section = configuration.GetSection(sectionToEncrypt);
       
        if (!section.SectionInformation.IsProtected)
        {
            section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
            section.SectionInformation.ForceSave = true;
            configuration.Save(ConfigurationSaveMode.Modified);

        }
    }

Part of my lack of understanding that prompted this question was not knowing that you can safely create custom actions in managed code using DTF. Documentation is sparse on DTF, but once you get it working, it is great.

I found that this only worked if I scheduled the custom action after InstallFinalize.

Here is the WiX config to make it happen:

<InstallExecuteSequence>
  <Custom Action="EncryptConfigurationFiles" After="InstallFinalize" />
</InstallExecuteSequence>

<Fragment>
    <Binary Id="YourProject.CustomActions.dll" SourceFile="$(var.YourProject.CustomActions.TargetDir)$(var.YourProject.CustomActions.TargetName).CA.dll" />
    <CustomAction Id="EncryptConfigurationFiles" BinaryKey="YourProject.CustomActions.dll" DllEntry="EncryptConfig" Return="check" />
</Fragment>

These blogs/sites helped me get there and much of the code from above was derived from them:

http://geekswithblogs.net/afeng/Default.aspx http://blog.torresdal.net/2008/10/24/WiXAndDTFUsingACustomActionToListAvailableWebSitesOnIIS.aspx http://blogs.msdn.com/jasongin/archive/2008/07/09/votive-project-platform-configurations.aspx

@PITADeveloper... Thanks for the response. I found that I did not need to load the assembly to encrypt the config file.

If you use this, you should use a try catch and return an ActionResult... The above is pseudo-code.

Finally, I'm using the DataProtectionConfigurationProvider. For the RSA Provider, I think there are a couple more hoops to jump through.

I hope this helps someone!

like image 90
JasonS Avatar answered Oct 22 '22 20:10

JasonS


You should be able to do it within a custom action. The catch that I've found is that loading an assembly for an ExeConfigurationFileMap will throw an exception, but you can handle that by adding an AssemblyResolve handler to the AppDomain. This is kind of a hack-up from a rich-client app I wrote to encrypt/decrypt protected configuration sections using a machine encryption key. It's probably not the prettiest code, but I'm hoping you can get the picture from it. This code assumes that you have the ProtectionProvider you want to use defined in the config file.

//class global
static System.Reflection.Assembly DefiningAssembly;

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

static System.Reflection.Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
     return DefiningAssembly;
}

Then, you can load your configuration like this:

DefiningAssembly = System.Reflection.Assembly.LoadFrom("path to defining assembly for config");

//Set the Configuration using an ExeConfigurationFileMap - This works for any .config file.
ExeConfigurationFileMap CfgMap = new ExeConfigurationFileMap();
CfgMap.ExeConfigFilename = "path to config file";
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(CfgMap, ConfigurationUserLevel.None);

List<string> DefiningAssemblyTypes = new List<string>();
foreach (System.Type type in DefiningAssembly.GetExportedTypes())
{
     DefiningAssemblyTypes.Add(type.Name);
}

foreach (ConfigurationSection tempSection in config.Sections)
{
     if (DefiningAssemblyTypes.Contains(tempSection.ElementInformation.Type.Name))
     {
          section = tempSection;
          break;
     }
}
ProtectionProviderName = section.SectionInformation.ProtectionProvider.Name;
section.SectionInformation.ProtectSection(ProtectionProviderName);
config.Save(ConfigurationSaveMode.Minimal, true);

I hope this helps you, best of luck.

like image 25
AJ. Avatar answered Oct 22 '22 20:10

AJ.