I want encrypt and decrypt data using RSACryptoServiceProvider in c# in wp8 project. I am creating asymmetric keys as :
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = "MyContainer";
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(parameters);
Now I want do encrypt data. I am doing:
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = "MyContainer";
RSACryptoServiceProvider obj = new RSACryptoServiceProvider(parameters);
byte[] a = Generic.RSAEncrypt(ByteConverter.GetBytes(s[0]),
obj.ExportParameters(false), false);
public static byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo,
bool DoOAEPPadding)
{
try {
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider.
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = "TCSContainer";
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(parameters))
{
//Import the RSA Key information. This only needs
//to include 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 (CryptographicException e) {
//Catch and display a CryptographicException
//to the console.
//Console.WriteLine(e.Message);
return null;
}
}
Now I am getting exception while encypting:
RSA.EncryptSystem.Security.Cryptography.CryptographicException : Bad length in RSACryptoserviceProvider.
Stacktrace is:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.RSACryptoServiceProvider.EncryptKey(SafeKeyHandle pKeyContext, Byte[] pbKey, Int32 cbKey, Boolean fOAEP, ObjectHandleOnStack ohRetEncryptedKey)
at System.Security.Cryptography.RSACryptoServiceProvider.Encrypt(Byte[] rgb, Boolean fOAEP)
at WindowsAppmart.Generic.RSAEncrypt(Byte[] DataToEncrypt, RSAParameters RSAKeyInfo, Boolean DoOAEPPadding)
and message is Bad Length.
I am not getting where can I go wrong?
RSA is only meant to be used for encrypting small amounts of data. The exact amount you can encrypt depends on the key length + the amount used by the padding. A 1024 bit key would allow for a bit above 100 bytes.
Since RSA is quite slow, the usual way to encrypt large messages is using hybrid encryption. In hybrid encryption you use a fast symmetric encryption algorithm (like AES) for encrypting the data with a random key. The random key is then encrypted with RSA and send along with the symmetric key encrypted data.
This indicates that the amound of data you are trying to encrypt is too long. You should encrypt it in smaller bulks.
Related
I have below code to encrypt and decrypt the message in c#. when i am trying to run it is giving an exception ie "The data to be decrypted exceeds the maximum for this modulus of 256 bytes"
public static void Main(string[] args)
{
X509Certificate2 cert = new X509Certificate2(#"C:\Data\ABC-rsa-public-key-certificate.cer");
string encryptedText = EncrypIt("Hello", cert);
string decryptedText = DecrptIt(encryptedText, cert);
System.Console.WriteLine(decryptedText);
}
public static string EncrypIt(string inputString, X509Certificate2 cert)
{
RSACryptoServiceProvider publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] plainBytes = Encoding.UTF8.GetBytes(inputString);
byte[] encryptedBytes = publicKey.Encrypt(plainBytes, false);
string encryptedText = Encoding.UTF8.GetString(encryptedBytes);
return encryptedText;
}
public static string DecrptIt(string encryptedText, X509Certificate2 cert)
{
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] encryptedBytes = Encoding.UTF8.GetBytes(encryptedText);
byte[] decryptedBytes = privateKey.Decrypt(encryptedBytes, false);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
return decryptedText;
}
Several problems:
RSA by default only encrypts one block. It's not suitable for long messages. You shouldn't encrypt the message itself with RSA. Generate a random AES key and encrypt the key with RSA and the actual message with AES.
You must use a binary safe encoding such as Hex or Base64 for the ciphertext. Using UTF-8 corrupts the data since it doesn't allow arbitrary byte sequences.
UTF-8 is designed to encode text, so it's fine for your plaintext.
Use OAEP, the old 1.5 padding mode is not secure. i.e. pass true as second parameter to Encrypt/Decrypt. (Technically it's possible to use it securely, but it's tricky and I wouldn't recommend it)
As a further note, once you use AES, there are some more pitfalls: 1) Use a MAC in an encrypt-then-mac scheme, else active attacks including padding-oracles will break your code 2) Use a random IV that's different for each message
RSA should not be used to encrypt this kind of data. You should be encrypting your data with a symmetric key like AES, then encrypting the symmetric key with RSA.
I'm trying to encrypt an array of 256 bytes by using RSACryptoServiceProvider but I'm getting an exception of "Bad Length" each time.
byte[] seed = new byte[256];
byte[] cypherSeed = new byte[256];
RNGCryptoServiceProvider gen = new RNGCryptoServiceProvider();
gen.GetBytes(seed);
using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(2560))
{
RSAParameters param = new RSAParameters();
param.Exponent = this.exponent;
param.Modulus = this.modulus;
// set public keys
rsaCryptoServiceProvider.ImportParameters(param);
cypherSeed = rsaCryptoServiceProvider.Encrypt(seed, false);
}
What am I doing wrong??!!
You forgot to account for the padding. The message size is smaller than the key size because RSA needs the rest of the block for padding.
If you use PKCS#1 v1.5 padding, you need 11 bytes of padding, if you use the stronger OAEP padding, you need 2*hashsize + 2 bytes as padding.
See RSACryptoServiceProvider.Encrypt Method
You should also consider using hybrid encryption, where you encrypt a random key with RSA, and the actual message with that key.
I have a requirement, where I need to encrypt my connection string in one application and decrypt it in another. With this in mind, I save the public key and private keys in App.Config of the application respectively.
Now, shouldn't RSA should give me same encrypted string with same keys which I use?
I get different encrypted strings all the time, with same keys used.!! Please help me to clear the confusion. I am not understanding how I can solve this problem, that I get BAD Data exception if I use the saved encrypted string, as every time the encryption gives me different encrypted strings.
Here is my code:
private string connecString;
private RSACryptoServiceProvider rsaEncryptDecrypt;
public EncryptAndDecrypt(string connecString)
{
this.connecString = connecString;
this.rsaEncryptDecrypt = new RSACryptoServiceProvider(4096);
}
public string EncryptTheConnecString(string publicKeyValue)
{
byte[] encryptedData;
rsaEncryptDecrypt.FromXmlString(publicKeyValue);
byte[] message = Encoding.UTF8.GetBytes(connecString);
encryptedData = rsaEncryptDecrypt.Encrypt(message, false);
return Convert.ToBase64String(encryptedData);
}
public string DecryptTheConnecString(string privateKeyValue, string encrystr)
{
byte[] decryptedData;
rsaEncryptDecrypt.FromXmlString(privateKeyValue);
byte[] message = Convert.FromBase64String(encrystr);
decryptedData = rsaEncryptDecrypt.Decrypt(message, false);
return Encoding.UTF8.GetString((decryptedData));
}
Thank you in advance.
Update 1:
I used
UnicodeEncoding ByteConverter = new UnicodeEncoding();
ByteConverter.GetBytes("data to encrypt");
//Which is not Connection string but a small test str
Still I see that the encrypted data is changing everytime.
But the Bad Data error is no more seen. Yet I cannot use UTF16(UnicodeEncoding) over Encoding.UTF8 because it cannot encrypt the huge string like connection string and throws an exception:
CryptographicException: Key not valid for use in specified state.
Update 2:
I could solve the problem of bad data by using UTF8Encoding ByteConverter = new UTF8Encoding(); and then doing ByteConverter .GetString("HUGE STRING");
It can happen because of Random Padding.
In general the answer to your question is yes, it should always produce the same result if the same parameters are given.
The best way to tackle these issues is to stay as close to the best practice code as possible, currently you a using the crypto provider slightly different than the framework docs propose, see the following:
static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
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;
}
This is an excerpt from the official MSDN doc:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx
First try and adopt the best practice and then see if this issue still comes up.
I'd like to encrypt very little data (15 bytes to be exact) into a as short as possible (optimally, no longer than 16 bytes) message using a public key cryptography system.
The standard public key system, RSA, unfortunately produces messages as big as its keys, that is about 100 bytes, depending on key size.
To make things more difficult, I can only use .NET framework libraries, i.e. no third party.
I've read a little about elliptic curve cryptography in the wikipedia and the text there seems to suggest that key sizes there are usually much shorter than RSA keys.
Does this translate to short messages as well? Can the .NET ECDiffieHellmanCng class be used to de/encrypt messages? It seems to feature a different class structure then, say, RSA or the symmetric ciphers.
You can use ECDiffieHellman to encrypt messages. You have two options: Static-static ECDH and static-ephemeral ECDH:
For static-static ECDH the receiver will need to know the senders public key (this might or might not be an option in your application). You should also have some data that is unique for this message (it might be a serial-number you get from somewhere else in the protocol or database-row or whatever or it might be a nonce). You then use ECDH to generate a secret key and use that to encrypt your data. This will give you your desired encrypted data length of 16 bytes, but it is not completely asymmetric: the encryptor is also able to decrypt the messages (again: this might or might not be a problem in your application).
Static-ephemeral is a bit different: here the encryptor generates a temporary (ephemeral) EC keypair. He then uses this keypair together with the receivers public key to generate a secret key which can be used to encrypt the data. Finally he sends the public key of the ephemeral keypair to the receiver together with the encrypted data. This might fit better into your application, but the complete encrypted data will now be 2*32+16=80 bytes using ECDH-256 and AES (as GregS notes you can save 32 bytes by only sending the x-coordinate of the public-key, but I do not believe that .NET exposes the functionality to recalculate the y-coordinate).
Here is a small class that will do static-static ECDH:
public static class StaticStaticDiffieHellman
{
private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
{
privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
privateKey.HashAlgorithm = CngAlgorithm.Sha256;
privateKey.SecretAppend = nonce;
byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
byte[] key = new byte[16];
Array.Copy(keyAndIv, 0, key, 0, 16);
byte[] iv = new byte[16];
Array.Copy(keyAndIv, 16, iv, 0, 16);
Aes aes = new AesManaged();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
return aes;
}
public static byte[] Encrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] data){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateDecryptor().TransformFinalBlock(encryptedData,0, encryptedData.Length);
}
}
// Usage:
ECDiffieHellmanCng key1 = new ECDiffieHellmanCng();
ECDiffieHellmanCng key2 = new ECDiffieHellmanCng();
byte[] data = Encoding.UTF8.GetBytes("TestTestTestTes");
byte[] nonce = Encoding.UTF8.GetBytes("whatever");
byte[] encryptedData = StaticStaticDiffieHellman.Encrypt(key1, key2.PublicKey, nonce, data);
Console.WriteLine(encryptedData.Length); // 16
byte[] decryptedData = StaticStaticDiffieHellman.Decrypt(key2, key1.PublicKey, nonce, encryptedData);
Console.WriteLine(Encoding.UTF8.GetString(decryptedData));
ECDiffieHellmanCNG is a derivation of the original Diffie-Hellman Key Exchange Protocol.
It is not intended for encrypting messages but rather calculating the same secret value on both ends.
Here is some information on ECDiffieHellmanCNG and its purpose.
I got a module which RSA encrypts the data and passes on to the C#.
C# needs to decrypt it based on the public key (64 bit encoded) and the passed token.
I have token , 64 bit encoded public key, can some help me get with the sample to get started.
All I know from Java end is, it is using. I have got the result from Java end and need to write a parser in C# to decrypt this. I get both public key and token as a string value.
Cipher cipher = Cipher.getInstance(ALGORITHM); //Algorithm = "RSA"
cipher.init(Cipher.DECRYPT_MODE, key);
Thanks
To get started, you'll need the private key to decrypt the message. By "public key (64 bit encoded)", I'm guessing what you really have is a Base-64–encoded certificate, with a header line that says "----- BEGIN CERTIFICATE-----" and a footer that says "-----END CERTIFICATE-----".
If that's correct, you'll need to find the private key. This is sometimes stored in a PKCS #12 format file, with a ".p12" or ".pfx" extension. You'll need a password to access the private key if it is stored in such a file.
Alternatively, OpenSSL and other utilities use private key files that can be Base-64–encoded or binary. These have a variety of extensions, and may or may not be password-protected. If the file that you have has a header line of "-----BEGIN RSA PRIVATE KEY-----" or "-----BEGIN PRIVATE KEY-----", that is actually the private key.
Finally, Windows can store private keys in its internal key store.
When you clarify the location of the private key, please update your question.
If the private key is used on the Java side, it may be an attempt to perform a digital signature. While all of several Java providers I've tested produce correct results when (ab)used this way, if you are doing a signature, the Signature class should be used. The C# code should use a signature object to "verify" the signature as well.
Encryption is performed with the private key. Since the public key is public, anyone can decrypt the message; i.e., the message is not confidential. Public keys are used by recipients to verify signed messages.
Check this code out.
public static string Decrypt(string inputText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(inputText.Replace(" ","+"));
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
using (ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
{
using (MemoryStream memoryStream = new MemoryStream(encryptedData))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
return Encoding.Unicode.GetString(plainText, 0, decryptedCount);
}
}
}