Freigeben über


Getting a “System.ArgumentException: Value was invalid” when trying to sign data using SHA256CryptoServiceProvider

 

Here is the symptom:
1.    You are using RSACryptoServiceProvider for computing SHA-2 signatures.
2.    Doing this you get unhandled exceptions of type "System.ArgumentException" in mscorlib.dll saying "Value was invalid".
3.    A typical call that failed was:

byte[] signature = rsa.SignData(data, new SHA256CryptoServiceProvider());
4.    The SHA1CryptoServiceProvider did not reproduce the exception.
5.    Additionally you have FIPS policy enabled.
The environment might be Windows Vista and above with .Net Framework version 3.5 or above. The code snippet that reproduces this issue is:
namespace SignData
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };

            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                SHA256CryptoServiceProvider ha = new SHA256CryptoServiceProvider();
                byte[] signature = rsa.SignData(data, ha);

                if (rsa.VerifyData(data, new SHA256CryptoServiceProvider(), signature))
                {
                    Console.WriteLine("RSA-SHA256 signature verified");
                }
                else
                {
                    Console.WriteLine("RSA-SHA256 signature failed to verify");
                }
            }
        }
    }
}

This code is directly referenced from the blog https://blogs.msdn.com/shawnfa/archive/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures.aspx and my intension is to show the exception.

The exception that you get is as shown in the screen-shot below:

blog

that says:

System.ArgumentException was unhandled
  Message=Value was invalid.
  Source=mscorlib
  StackTrace:
       at System.Security.Cryptography.Utils.ObjToOidValue(Object hashAlg)
       at System.Security.Cryptography.RSACryptoServiceProvider.SignData(Byte[] buffer, Object halg)

The root cause for this is that the CryptoConfig does not understand SHA256CryptoServiceProvider. It was added as part of the green bits in .NET 3.5, and due to layering restrictions the red bits (such as mscorlib.dll where RSACryptoServiceProvider lives) does not know about its existence.
This is listed in the blog https://blogs.msdn.com/shawnfa/archive/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures.aspx.

If you are using .Net Framework 4.0 then the resolution is to modify the “machine.config” file at:

%WINDIR%\Microsoft.NET\Framework\v4.0.xxxxx\ CONFIG  -> for x86
%WINDIR%\Microsoft.NET\Framework64\v4.0.xxxxx\CONFIG -> for x64

If you are using .Net Framework 3.5 then the resolution is to modify the “machine.config” file at:

%WINDIR%\Microsoft.NET\Framework\v2.0.xxxxx\ CONFIG  -> for x86
%WINDIR%\Microsoft.NET\Framework64\v2.0.xxxxx\CONFIG -> for x64

Here is the entry that you need to make at the "machine.config" file for supporting SHA256CryptoServiceProvider, SHA256Cng, SHA384CryptoServiceProvider, SHA384Cng, SHA512CryptoServiceProvider, and SHA512Cng.

  <mscorlib>
    <cryptographySettings>
      <cryptoNameMapping>
        <cryptoClasses>
          <cryptoClass SHA256CSP="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA256CNG="System.Security.Cryptography.SHA256Cng, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA384CSP="System.Security.Cryptography.SHA384CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA384CNG="System.Security.Cryptography.SHA384Cng, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA512CSP="System.Security.Cryptography.SHA512CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA512CNG="System.Security.Cryptography.SHA512Cng, System.Core, Version=4.0.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" />
        <nameEntry name="SHA256Next" class="SHA256CNG" />
        <nameEntry name="SHA256Cng" class="SHA256CNG" />
        <nameEntry name="System.Security.Cryptography.SHA256Cng" class="SHA256CNG" />
        <nameEntry name="SHA384" class="SHA384CSP" />
        <nameEntry name="SHA384CryptoServiceProvider" class="SHA384CSP" />
        <nameEntry name="System.Security.Cryptography.SHA384CryptoServiceProvider" class="SHA384CSP" />
        <nameEntry name="SHA384Next" class="SHA384CNG" />
        <nameEntry name="SHA384Cng" class="SHA384CNG" />
        <nameEntry name="System.Security.Cryptography.SHA384Cng" class="SHA384CNG" />
        <nameEntry name="SHA512" class="SHA512CSP" />
        <nameEntry name="SHA512CryptoServiceProvider" class="SHA512CSP" />
        <nameEntry name="System.Security.Cryptography.SHA512CryptoServiceProvider" class="SHA512CSP" />
        <nameEntry name="SHA512Next" class="SHA512CNG" />
        <nameEntry name="SHA512Cng" class="SHA512CNG" />
        <nameEntry name="System.Security.Cryptography.SHA512Cng" class="SHA512CNG" />
      </cryptoNameMapping>
      <oidMap>
        <oidEntry OID="2.16.840.1.101.3.4.2.1" name="SHA256" />
        <oidEntry OID="2.16.840.1.101.3.4.2.1" name="SHA256Next" />
        <oidEntry OID="2.16.840.1.101.3.4.2.2" name="SHA384" />
        <oidEntry OID="2.16.840.1.101.3.4.2.2" name="SHA384Next" />
        <oidEntry OID="2.16.840.1.101.3.4.2.3" name="SHA512" />
        <oidEntry OID="2.16.840.1.101.3.4.2.3" name="SHA512Next" />
      </oidMap>
    </cryptographySettings>
  </mscorlib>

Much about this “machine.config” file is stated at the blog https://blogs.msdn.com/b/shawnfa/archive/2008/12/02/cryptoconfig.aspx. However making this change will pass the above code on Windows Vista and above OS's.
After you make the changes you can do a small test using the code below:
namespace SignData
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };

            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                byte[] signature = rsa.SignData(data, "xxxxxxx"); // "xxxxxxx" stands for SHA256 or SHA256Next, SHA384, SHA384Next, SHA512, SHA512Next

                if (rsa.VerifyData(data, "xxxxxx", signature)) // "xxxxxxx" stands for SHA256 or SHA256Next, SHA384, SHA384Next, SHA512, SHA512Next
                {
                    Console.WriteLine("RSA-SHAxxx signature verified");
                }
                else
                {
                    Console.WriteLine("RSA-SHAxxx signature failed to verify");
                }
            }
        }
    }
}

References:
https://blogs.msdn.com/shawnfa/archive/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures.aspx
https://blogs.msdn.com/b/shawnfa/archive/2008/12/02/cryptoconfig.aspx
https://blogs.msdn.com/b/winsdk/archive/2009/11/04/is-rijndaelmanaged-class-fips-complaint.aspx
https://blogs.msdn.com/b/winsdk/archive/2010/05/28/behaviour-of-aescryptoserviceprovider-class-with-fips-policy-set-unset.aspx