Wednesday, 10 November 2010

key not valid for use in specified state

Something that a colleague recently found out that may be useful if you ever need to use the RSA Encryption provider in the .Net Framework.

If you try to encrypt data that exceeds 245 bytes you get an error "key not valid for use in specified state". This is a little misleading considering the cause, which I found here in the end: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce/

For those of you interested you can re-create with the following (this is a modification to the RSA Example from http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx).

If you uncomment the second 'Append' line and re-run it the provider fails with the "key not valid" exception.
using System;
using System.Security.Cryptography;
using System.Text;

class RSACSPSample
{

static void Main()
{
StringBuilder sb = new StringBuilder();
sb.Append("The quick brown fox jumped over the lazy dogs.");
//sb.Append("The quick brown fox jumped over the lazy dogs.");

Console.WriteLine(sb.Length.ToString());

try
{
//Create a UnicodeEncoder to convert between byte array and string.
UnicodeEncoding ByteConverter = new UnicodeEncoding();

//Create byte arrays to hold original, encrypted, and decrypted data.
byte[] dataToEncrypt = ByteConverter.GetBytes(sb.ToString());
byte[] encryptedData;
byte[] decryptedData;

//Create a new instance of RSACryptoServiceProvider to generate
//public and private key data.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{

//Pass the data to ENCRYPT, the public key information
//(using RSACryptoServiceProvider.ExportParameters(false),
//and a boolean flag specifying no OAEP padding.
encryptedData = RSAEncrypt(dataToEncrypt, RSA.ExportParameters(false), false);

//Pass the data to DECRYPT, the private key information
//(using RSACryptoServiceProvider.ExportParameters(true),
//and a boolean flag specifying no OAEP padding.
decryptedData = RSADecrypt(encryptedData, RSA.ExportParameters(true), false);

//Display the decrypted plaintext to the console.
Console.WriteLine("Decrypted plaintext: {0}", ByteConverter.GetString(decryptedData));
}
}
catch (ArgumentNullException)
{
//Catch this exception in case the encryption did
//not succeed.
Console.WriteLine("Encryption failed.");

}

Console.ReadKey();
}

static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{

//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);

//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
return encryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);

return null;
}

}

static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] decryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);

//Decrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
return decryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.ToString());

return null;
}

}
}
You can force a key size in the constructor for the provider to allow more data, but this slows the algorithm down considerably.

Hopefully this may be useful to someone at some point :

No comments: