Dusted
Codes

Programming Adventures

How to use RSA in .NET: RSACryptoServiceProvider vs. RSACng and good practise patterns

Published

Comments

dotnet rsa security asymmetric-encryption cryptography

In my last blog post I wrote a little crash course on RSA and how it works without looking into any specific language implementations. Today I'd like to explore the native implementations of .NET and the new RSACng class which has been introduced with .NET Framework 4.6.

In .NET 4.6 you'll find three native RSA classes:

  1. RSA
  2. RSACryptoServiceProvider
  3. RSACng

RSA Class

The RSA class in System.Security.Cryptography is an abstract class which cannot be instantiated itself. It is the base class for all other RSA implementations and exists since .NET 1.1 in the mscorlib assembly.

It derives from the abstract class AsymmetricAlgorithm, which itself implements IDisposable. This means every instance of an RSA implementation should be disposed after its usage to free up memory as soon as possible.

The base class defines many methods, but most likely you will be interested in one of these which come with the default RSA contract:

  1. Encrypting data
  2. Decrypting cipher data
  3. Signing data
  4. Signing a hash of data
  5. Validating signed data
  6. Validating a signed hash
  7. A factory method to instantiate an implementation of RSA

Differences between .NET Frameworks

.NET 3.5 and earlier

In .NET 3.5 and earlier the RSA class was much smaller than it is today. It didn't have a contract for signing and validating data and only exposed two methods for encyrpting and decrypting a value:

Also notice how the methods accept a data object but no indication of which padding system should be used. Using an encyrption padding is critical to the security of your RSA implementation and therefore is somewhat lacking in the base contract.

After .NET 4.0

Starting with the .NET 4.0 framework the RSA class has been significantly extended. In addition to all the signing methods it received two new methods for encrypting and decrypting a message:

  • public virtual byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
  • public virtual byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)

Interestingly they are not mentioned in the official MSDN documentation on the web, however when I decompile .NET 4.0's mscorlib I can see the two virtual methods:

Encrypt and Decrypt methods in .NET C# RSA Class, Image by Dustin Moris Gorski

This was a great addition for two reasons in particular:

  1. It allows you to specify which padding should be used.
  2. It matches the implementation of the RSACryptoServiceProvider. This is very nice, because the RSACryptoServiceProvider never bothered to implement the EncryptValue and DecryptValue methods and made it impossible to program against the native RSA contract in previous .NET versions.

Factory methods

The RSA class also implements two static factory methods to create an instance of RSA:

  1. public static RSA Create()
  2. public static RSA Create(string algName)

In all versions of .NET the default implementation is the RSACryptoServiceProvider:

using (var rsa = RSA.Create())
{
    Console.WriteLine(rsa.GetType().ToString());
    // Returns System.Security.Cryptography.RSACryptoServiceProvider
}

Overriding the default implementation

The factory methods are designed to work with the CryptoConfig setting from the machine.config file.

In order to override the default implementation you need to map a friendly algorithm name to a specific cryptography class.

Be aware that you have different machine.config files for each architecture and .NET framework:

  • 32-bit
    %windir%\Microsoft.NET\Framework\{.net version}\config\machine.config
  • 64-bit
    %windir%\Microsoft.NET\Framework64\{.net version}\config\machine.config

For example if you have a 64-bit application built with .NET 4.0 you need to modify the machine.config under this path:
%windir%\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config

For this demo I will map "SomeCustomFriendlyName" to the native RSACng class:

<mscorlib>
  <cryptographySettings>
    <cryptoNameMapping>
      <cryptoClasses>
        <cryptoClass
         MyRSAImplementation="System.Security.Cryptography.RSACng,
          System.Core, Version=4.0.0.0, Culture=neutral,
          PublicKeyToken=b77a5c561934e089" />
      </cryptoClasses>
      <nameEntry
        name="SomeCustomFriendlyName"
        class="MyRSAImplementation" />
    </cryptoNameMapping>
  </cryptographySettings>
</mscorlib>

Now when I run this...

using (var rsa = RSA.Create("SomeCustomFriendlyName"))
{
    Console.WriteLine(rsa.GetType().ToString());
}

...it will output "System.Security.Cryptography.RSACng".

If you don't specify a friendly algorithm name then the default Create() method will call RSA.Create("System.Security.Cryptography.RSA") under the covers.

Hence adding another nameEntry for "System.Security.Cryptography.RSA":

<mscorlib>
  <cryptographySettings>
    <cryptoNameMapping>
      <cryptoClasses>
        <cryptoClass
          MyRSAImplementation="System.Security.Cryptography.RSACng,
          System.Core, Version=4.0.0.0, Culture=neutral,
          PublicKeyToken=b77a5c561934e089" />
      </cryptoClasses>
      <nameEntry
        name="SomeCustomFriendlyName"
        class="MyRSAImplementation" />
      <nameEntry
        name="System.Security.Cryptography.RSA"
        class="MyRSAImplementation" />
    </cryptoNameMapping>
  </cryptographySettings>
</mscorlib>

...will now also output "System.Security.Cryptography.RSACng" when I run RSA.Create().GetType().ToString().

RSACryptoServiceProvider vs. RSACng

Where to find

RSACryptoServiceProvider exists in mscorlib since .NET 1.1 while RSACng is a very recent addition with .NET 4.6 and lives in System.Core.

CoreFX is open source and you can browse any implementation as well as the RSACng class on GitHub.

Implementation

Both classes are sealed and derive from the base RSA class and implement their members. However, both classes throw a NotSupportedException when calling EncryptValue and DecryptValue. You are forced to use the Encrypt and Decrypt methods which accept a padding.

Both classes are FIPS compliant!

Also worth mentioning is that both classes use the OS's underlying CSP (Crypto Service Provider) and CNG (Cryptography Next Generation) providers which are unmanaged code and should have considerably similar performance.

Difference

In short, the CNG implementation is...

...the long-term replacement for the CryptoAPI. CNG is designed to be extensible at many levels and cryptography agnostic in behavior.

One difference which you will quickly notice is that the key in RSACng is managed by the CngKey class and can be injected into the constructor of RSACng, while RSACryptoServiceProvider is tied to its own key operations.

Support

CNG is supported beginning with Windows Server 2008 and Windows Vista. RSACng requires .NET framework 4.6 (or higher).

The Crypto API was first introduced in Windows NT 4.0 and enhanced in subsequent versions.

Good practise patterns

After all it doesn't really matter which implementation you choose if you abstract it away and program against a contract. This is good practise anyway and luckily .NET offers the abstract RSA class for this purpose out of the box.

Constructor injection vs. Factory method

Constructor injection is the de facto standard pattern for dependency injection in most cases. However, when you deal with an object which implements the IDisposable interface then constructor injection can be responsible for keeping the object longer alive than it needs to be.

Image I have this class:

public class MyClass
{
    private readonly RSA _rsa;

    public MyClass(RSA rsa)
    {
        _rsa = rsa;
    }

    public void sendSecureMessage(string message)
    {
        byte[] data;
        // Convert the message into data

        _rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);

        // More business logic
    }
}

Whatever happens after _rsa.Encrypt(...) doesn't require the RSA object any longer and it should get disposed immediately.

I could call _rsa.Dispose(); afterwards, but this would be a bad code smell, because:

  1. MyClass is not the owner of the RSA instance. It didn't create it and therefore shouldn't dispose it.
  2. The RSA instance could have been injected somewhere else as well, therefore disposing it would cause a bug.
  3. There is a benefit of the using statement and the constructor injection pattern doesn't allow me to make use of it

This means we are better off by using a different dependency injection pattern. In this instance the factory pattern is more suitable:

public interface IRSAFactory
{
    RSA CreateRSA();
}

public class MyClass
{
    private readonly IRSAFactory _rsaFactory;

    public MyClass(IRSAFactory rsaFactory)
    {
        _rsaFactory = rsaFactory;
    }

    public void SendSecureMessage(string message)
    {
        byte[] data;
        // Convert the message into data

        using (var rsa = _rsaFactory.CreateRSA())
        {
            rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
        }

        // More business logic
    }
}

I have created an IRSAFactory interface which defines a contract to create a new instance of RSA. Now I can inject this factory into MyClass and conveniently create a new RSA object on the fly and encapsulate it with the using statement to properly dispose it afterwards.

Why not using the native RSA.Create() factory method?

What is wrong with the native factory method? Why didn't I just do this:

public class MyClass
{
    public void SendSecureMessage(string message)
    {
        byte[] data;
        // Convert the message into data

        using (var rsa = RSA.Create())
        {
            rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
        }

        // More business logic
    }
}

Theoretically this is absolutely fine, but it makes it more difficult to provide an alternative implementation in my unit test suite, because the native factory is designed to work with the machine.config and cannot be changed programmatically!

Wrapper for RSA

Alternatively I could have created a wrapper for the RSA class like this:

public abstract class RSAWrapper : RSA
{
    private static RSA _overridenDefaultRSA = null;

    public static void OverrideDefaultImplementation(RSA rsa)
    {
        _overridenDefaultRSA = rsa;
    }

    public static new RSA Create()
    {
        return _overridenDefaultRSA ?? RSA.Create();
    }
}

...and then use it like the original class:

public class MyClass
{
    public void SendSecureMessage(string message)
    {
        byte[] data;
        // Convert the message into data

        using (var rsa = RSAWrapper.Create())
        {
            rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
        }

        // More business logic
    }
}

This would allow me to override the default implementation from the machine.config by calling into OverrideDefaultImplementation(RSA rsa) from my unit tests.

I personally prefer the first approach though, for the following reasons:

  1. By injecting an IRSAFactory object into MyClass it is obvious from the outside which dependencies MyClass has
  2. It allows me to provide different mocks and stubs for different unit tests while running them in parallel. This would be very tricky with the static factory method.
  3. At some point I'd have to initialise the RSA key by using RSA.FromXmlString or RSA.ImportParameters. With the IRSAFactory (which is an abstract factory btw) I could provide additional methods to do this and test the interaction between them as well.

I hope this was useful and that I could shed more light on RSA in .NET.

There are a lot of resources on the internet showing how to use the RSACryptoServiceProvider and therefore I didn't want to re-iterate over the same topic again and focus more on some patterns beyond the default examples.