Robin
Robin

Reputation: 181

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!

Upvotes: 3

Views: 2085

Answers (2)

jebabli ilyes
jebabli ilyes

Reputation: 11

if you encrypt and save your config file programatically , the Generated Key in C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys is not Exportable.

when you try Export Key With

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

this error is fired because the default generated key file not exportable (without Private Key) :

Key not valid for use in specified state.

for this, you must Create the CspParameters object and set the key container before encrypt and Save like this :

dont forget add this on your configfile

<configProtectedData>
    <providers>
      <add keyContainerName="MyKeyName"
               useMachineContainer="true"
               name="MyKeyName"
               type="System.Configuration.RsaProtectedConfigurationProvider,
                 System.Configuration,
                 Version=2.0.0.0,
                 Culture=neutral,
                 PublicKeyToken=b03f5f7f11d50a3a"/>
    </providers>
  </configProtectedData>

and This is Function for Encrypt or Decrypt ALL ConfigFile with specific keyname, it generate several catched exception because some sections are not cryptable.

 public static Configuration EncryptorDecryptConfigFile(string ConfigFile, string MyKeyName, bool Encrypt = true)
        {
            try
            {
                Configuration config = OpenConfiguration(ConfigFile);
                CspParameters param = new System.Security.Cryptography.CspParameters();
                param.Flags = System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore;
                param.KeyContainerName = MyKeyName;
                foreach (ConfigurationSectionGroup configSectiongroup in config.SectionGroups)
                {
                    try
                    {
                        foreach (ConfigurationSection configSection in configSectiongroup.Sections)
                        {
                            try
                            {
                                if (configSection != null)
                                {
                                    if (Encrypt)
                                        if (!configSection.SectionInformation.IsProtected)
                                            configSection.SectionInformation.ProtectSection(MyKeyName);
                                        else
                                        if (configSection.SectionInformation.IsProtected)
                                            configSection.SectionInformation.UnprotectSection();
                                }
                            }
                            catch (Exception)
                            {
                            }
                        }
                    }
                    catch (Exception bb)
                    {
                    }
                }
                foreach (string key in config.Sections.Keys)
                {
                    try
                    {
                        ConfigurationSection configSection = config.Sections[key];
                        if (configSection != null)
                        {
                            if (Encrypt)
                                if (!configSection.SectionInformation.IsProtected)
                                    configSection.SectionInformation.ProtectSection(MyKeyName);
                                else
                                if (configSection.SectionInformation.IsProtected)
                                    configSection.SectionInformation.UnprotectSection();
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
                return config;
            }
            catch (Exception ex)
            {
                string errorType = ex.GetType().FullName;
                return null;
            }
        }
 

Upvotes: 0

Matt
Matt

Reputation: 27005

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.

Upvotes: 2

Related Questions