CryptoConfig

The crypto config schema has been a bit of a hot topic around here lately, specifically around how to modify the CLR's machine.config to get custom crypto types registered with CryptoConfig.

Let's take a quick look at what CryptoConfig is first, and then we'll see how to customize its behavior.  CryptoConfig is a type in mscorlib which allows cryptography classes to be created from a string rather than using a hard coded type.  For instance, you can say:

HashAlgorithm hashAlgorithm = CryptoConfig.CreateFromName("SHA256Managed") as HashAlgorithm;

to create instances of crypto types. This means that rather than having to hard code algorithms and implementations into your assembly itself, you can accept the name of the algorithm as a configuration parameter to achieve some measure of crypto agility.  CryptoConfig is also the underlying mechanism that allows the algorithm factory methods to work.  For instance, the above snippet is more commonly written as:

HashAlgorithm hashAlgorithm = HashAlgorithm.Create("SHA256Managed");

CryptoConfig comes built in with names of algorithms that ship with the .NET Framework (with the exception of the new algorithms introduced in .NET 3.5 due to red bits / green bits restrictions).  You can also extend the names that CryptoConfig understands if you have your own algorithms that you would like to be createable by name.  In fact, you can even do this to get the .NET 3.5 algorithms registered.

The customizable algorithm name mappings are setup in the machine.config file in the config subdirectory of the CLR installation directory.  For instance, for the 32 bit .NET 2.0, 3.0, and 3.5 releases you would register your types in the %WINDIR%\Microsoft.NET\Framework\v2.0.50727\config\machine.config file.  (For the 64 bit versions, you would also need to modify the equivalent file in Framework64).

CryptoConfig looks for information in the configuration/mscorlib/cryptographySettings element of machine.config. If there are multiple mscorlib sections, then crypto config prefers one with a version attribute that matches the current runtime -- however, in general there is only one mscorlib element.

<configuration>

    <mscorlib>

        <cryptographySettings>

            <cryptoNameMapping>               

                <!-- name mappings -->

            </cryptoNameMapping>

            <oidMap>

                <!-- OID mappings -->

            </oidMap>

        </cryptographySettings>

    </mscorlib>

</configuration>

Name mappings are created in a nameMappings element under cryptographySettings.  In order to setup a name mapping, two steps are required:

  1. Map the implementation type to an alias by registering it as a crypto class
  2. Map the alias to the name that you wish to use in CryptoConfig to create an instance of the class.

For example, imagine that you want to register the SHA256CryptoServiceProvider type that shipped in .NET 3.5 to be able to be created with the strings "SHA256", "SHA256CryptoServiceProvider", and "System.Security.Cryptography.SHA256ServiceProvider".  The first step is to register SHA256CryptoServiceProvider as a crypto class.  We can do this by creating a cryptoClasses node within the cryptoNameMapping element:

<cryptoClasses>

    <cryptoClass

        SHA256CSP="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

</cryptoClasses>

This creates an alias of SHA256CSP that refers to the SHA256CryptoServiceProvier type in System.Core.dll.  Note that the assemblies used in CryptoConfig must reside in the GAC; in this case System.Core.dll is in the GAC so registering SHA256CryptoServiceProvider is valid.

Now that we have created an alias we need to setup some names to create it with at runtime.  These names are created using nameEntry elements in the cryptoNameMapping element:

<nameEntry

    name="SHA256"

    class="SHA256CSP" />

<nameEntry

    name="SHA256CryptoServiceProvider"

    class="SHA256CSP" />

<nameEntry

    name="System.Security.Cryptography.SHA256CryptoServiceProvider"

    class="SHA256CSP" />

Each nameEntry element maps a string which CryptoConfig will accept to the alias for the type that it will create.  Here, we've setup entries that allow CryptoConfig to create a SHA256CryptoServiceProvier object via the names "SHA256", "SHA256CryptoServiceProvider", and "System.Security.Cryptography.SHA256CryptoServiceProvider".

If you're paying close attention, you'll notice that I mapped SHA256 to the .NET 3.5 SHA256CryptoServiceProvider class, even though the CLR already has a built in mapping for SHA256 to the SHA256Managed class.  In the case of a collision like this, machine.config entries take precedence over the built in mappings so these entries have the effect of changing the result of HashAlgorithm.Create("SHA256") from being a SHA256Managed object to being a SHA256CryptoServiceProvider object.

The final XML we ended up with for this example looks like this:

<configuration>

    <!-- ... other configuration data ... -->

    <mscorlib>

        <!-- ... other configuration data ... -->

        <cryptographySettings>

            <cryptoNameMapping>

                <cryptoClasses>

                    <cryptoClass

                        SHA256CSP="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

                </cryptoClasses>

                    <nameEntry

                        name="SHA256"

                        class="SHA256CSP" />

                    <nameEntry

                        name="SHA256CryptoServiceProvider"

                        class="SHA256CSP" />

                    <nameEntry

                        name="System.Security.Cryptography.SHA256CryptoServiceProvider"

                        class="SHA256CSP" />

            </cryptoNameMapping>

        </cryptographySettings>

    </mscorlib>

</configuration>

A couple of other small crypto config notes:

Above I mentioned that there is the ability to setup OID mappings - this allows you to add entries to the results returned from the CryptoConfig.MapNameToOid API.  These entries go in the oidMap element and are simple name -> OID pairs.  Like name map entries above, machine.config values take precedence over built-in values:

<oidMap>

    <oidEntry

        OID="2.16.840.1.101.3.4.2.1"

        name="SHA256" />

</oidMap>

Generally this is much less useful than the name mappings, since it really only allows you to access your custom OIDs via MapNameToOid -- however it's there if you do want to add custom name->OID pairs.

A final note is that although CryptoConfig itself doesn't have an API to modify the mappings at runtime, it is sometimes a lot more convenient to programmatically add crypto name mappings for your application at runtime than to worry about getting all the machine.config XML correctly added at installation time.  This can be done with the CryptoConfig2 class from the Security.Cryptography library on CodePlex.

CryptoConfig2 already has mappings for the .NET 3.5 types, but for the sake of an example the registration from the XML above could be done via code such as:

CryptoConfig2.AddAlgorithm(typeof(SHA256CryptoServiceProvider),

                           "SHA256",

                           "SHA256CryptoServiceProvider",

                           "System.Security.Cryptography.SHA256CryptoServiceProvider");

Since CryptoConfig2 does not modify the built-in CryptoConfig mappings, these aliases would be used like this:

HashAlgorithm hashAlgorithm = CryptoConfig2.CreateFromName("SHA256") as HashAlgorithm;

instead of going through the built in CryptoConfig or Create methods.