Search code examples
c#windowsencryptionrsaapp-config

How can I create an exportable RSA key with RsaProtectedConfigurationProvider with ProtectSection method in C#


In the following example I protect the "DemoWinApp.Properties.Settings" section of the "Sleutels.config" file.

    private static void toggleProtectionSleutelsConfig()
    {
        var fileMap = new ConfigurationFileMap(@"D:\Experimenten\ReadProtectedConfigFile\Sleutels.config");
        var configuration = ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
        var sectionGroup = configuration.GetSectionGroup("applicationSettings"); // This is the section group name, change to your needs
        var section = (ClientSettingsSection)sectionGroup.Sections.Get("DemoWinApp.Properties.Settings"); // This is the section name, change to your needs
        var setting = section.Settings.Get("SecretMessage"); // This is the setting name, change to your needs
        Console.WriteLine(setting.Value.ValueXml.InnerText);

        // Toggle beveiliging
        if (!section.SectionInformation.IsProtected)
        {
            //Protecting the specified section with the specified provider
            section.SectionInformation.ProtectSection("RSA");
        }
        else
        {
            section.SectionInformation.UnprotectSection();
        }
        section.SectionInformation.ForceSave = true;
        configuration.Save(ConfigurationSaveMode.Modified);


        Console.ReadKey();
    }

The contents of the "Sleutels.config" file is:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<sectionGroup name="applicationSettings"

     type="System.Configuration.ApplicationSettingsGroup, &#xD;&#xA;                    System, Version=2.0.0.0, Culture=neutral, &#xD;&#xA;                    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 name="RSA"
       type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0,&#xD;&#xA;                    Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,&#xD;&#xA;                    processorArchitecture=MSIL"
       keyContainerName="RobinsKeys"
       useMachineContainer="true" />
		</providers>
	</configProtectedData>
</configuration>

After running the code the "Sleutels.config" file is encrypted and a RSA key container is created in C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys

If I try to export the RSA key container with the commandline:

c:\windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -pc "RobinsKeys" –exp

Then I get the error message:

Exporting RSA Keys to file...
Key not valid for use in specified state.

This means that the RSA Key container is not marked as "exportable". If you would create an key container with the command line, then there is an optional parameter "-exp" to mark the key as exportable.

For example: aspnet_regiis -pc "RobinsKeys" -exp

Is this -exp option also available while using the section.SectionInformation.ProtectSection("RSA"); method in code or as an configuration option in the RSA provider section in the "Sleutels.config" configuration file?

Any help is appreciated!


Solution

  • To summarize the discussion, you need to create a RSA crypto container  programmatically  before you can use it to store RSA keys.

    The reason is that the RSAProtectedConfigurationProvider doesn't have an option to make the automatically created RSA key container  exportable.

    As you wrote in the chat, this workaround can be achieved by the following example code (I've added some output to the console, RSA parameters printed are explained here):

    void Main()
    {
       // Create the CspParameters object and set the key container
       // name used to store the RSA key pair.
       var cp = new System.Security.Cryptography.CspParameters();
       cp.Flags = System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore;
       cp.KeyContainerName = "RobinsKeys";
    
    
       // Create a new instance of RSACryptoServiceProvider that accesses
       // the key container MyKeyContainerName.
       // If it is not already there, it will create a new one, which is exportable.
       var myRSA = new System.Security.Cryptography.RSACryptoServiceProvider(cp);
        
       // print it on console
       Console.WriteLine($"=== Container: {cp.KeyContainerName} ===");
       Console.WriteLine(myRSA.ToXmlString(true).Replace("><", ">\n<")); 
    }
    

    which can be read in more detail here. The link provided also shows how to

    • generate and save a key pair
    • get a key from a container
    • delete a key from a container

    It might be useful to get a list of containers, this is discussed in a separate question.

    Some more information about key containers you can find in this Microsoft article, also about available aspnet_regiis parameters.

    Once a RSA container is created, it can be used by IIS. It is important to understand the difference between user-level and machine-level key containers, which is described in this documentation.


    Kindly let me know if anything is missing from the discussion, and I will update this answer.