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.
Related
I am using a Java based configuration management tool called Zuul which supports encrypting sensitive configuration information using various encryption schemes.
I have configured it to use below scheme for my data
AES (Bouncy Castle)
Name: PBEWITHSHA256AND128BITAES-CBC-BC
Requirements: Bouncy Castle API and JCE Unlimited Strength Policy Files
Hashing Algorithm: SHA256
Hashing Iterations: 1000
Now when reading my configuration data back, I need to decrypt the information before I can use it and the documentation provides below information around this topic.
The encrypted values produced by Jasypt (and thus Zuul) are are prefixed with the salt (usually 8 or 16 bytes depending on the algorithm requirements). They are then Base64 encoded. Decrypting the results goes something like this:
Convert the Base64 string to bytes
Strip off the first 8 or 16 bytes as the salt
Keep the remaining bytes for the encrypted payload
Invoke the KDF function with the salt, iteration count and the password to create the secret key.
Use the secret key to decrypt the encrypted payload
More details here: Zull Encryption wiki
Based on above details, I have written below code (and my knowledge around security is very limited)
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, saltBytes, iterations);
byte[] keyBytes = key.GetBytes(16);
AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
MemoryStream msDecrypt = new MemoryStream(encryptedBytes);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
StreamReader srDecrypt = new StreamReader(csDecrypt);
return srDecrypt.ReadToEnd();
}
I configured Zuul to use below password for the encryption
SimplePassword
And now I have an encrypted string given to me by Zuul and I need to decrypt it
p8C9hAHaoo0F25rMueT0+u0O6xYVpGIkjHmWqFJmTOvpV8+cipoDFIUnaOFF5ElQ
When I try to decrypt this string using above code, I get below exception
System.Security.Cryptography.CryptographicException : Padding is invalid and cannot be removed.
As I mentioned earlier, my knowledge around this topic is limited and I am not able to figure out if the information provided in the documentation is not enough, if I am doing something wrong while writing the decryption routine or should I be using bouncy castle for decryption as well.
Any help with this will be much appreciated.
According to Zuul documentation they are deriving both key and iv from the password/salt.
So you should derive 256+128 bits (i.e. 48 bytes), and use first 32 bytes as the key, and next 16 bytes as IV.
And this should be done in one operation, not as consequent calls to key.DeriveBytes.
I resorted to Bouncy Castle for decryption instead since that is used by Zuul as well.
Here is the code that works
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
const string algSpec = "AES/CBC/NoPadding";
const string algName = "PBEWITHSHA256AND128BITAES-CBC-BC";
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
char[] passwordChars = password.ToCharArray();
Asn1Encodable defParams = PbeUtilities.GenerateAlgorithmParameters(algName, saltBytes, iterations);
IWrapper wrapper = WrapperUtilities.GetWrapper(algSpec);
ICipherParameters parameters = PbeUtilities.GenerateCipherParameters(algName, passwordChars, defParams);
wrapper.Init(false, parameters);
byte[] keyText = wrapper.Unwrap(encryptedBytes, 0, encryptedBytes.Length);
return Encoding.Default.GetString(keyText);
}
I'm trying to develop a metro application on Windows 8.1 (c#) which will encrypt data with RSA.
The final goal is to encrypt an image (so a large byte array) with a given public key, and to send it away, to be decrypted on another platform (which will keep and use private key).
For the moment, for test purposes, I try to do all the job in my metro application : key creation, then crypt and decrypt data.
The code above works fine for a small string.
//Key creation
AsymmetricKeyAlgorithmProvider provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey key = provider.CreateKeyPair(1024);
IBuffer privateKey = key.Export(CryptographicPrivateKeyBlobType.Pkcs1RsaPrivateKey);
IBuffer publicKey = key.ExportPublicKey(CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
String publicKeyStr = CryptographicBuffer.EncodeToBase64String(publicKey);
String privateKeyStr = CryptographicBuffer.EncodeToBase64String(privateKey);
//Encrypt
IBuffer encryptionKeyBuffer = CryptographicBuffer.DecodeFromBase64String(publicKeyStr);
AsymmetricKeyAlgorithmProvider encodingProvider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey encryptKey = encodingProvider.ImportPublicKey(encryptionKeyBuffer, CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
IBuffer buf = CryptographicBuffer.ConvertStringToBinary("Hello World!", BinaryStringEncoding.Utf16BE);
var encrypted = CryptographicEngine.Encrypt(encryptKey, buf, null);
//DecrYpt
IBuffer decryptKeyBuffer = CryptographicBuffer.DecodeFromBase64String(privateKeyStr);
AsymmetricKeyAlgorithmProvider decryptionProvider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey decryptKey = decryptionProvider.ImportKeyPair(decryptKeyBuffer, CryptographicPrivateKeyBlobType.Pkcs1RsaPrivateKey);
IBuffer decryptedBuf = CryptographicEngine.Decrypt(decryptKey, encrypted, null);
Debug.WriteLine(CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16BE, decryptedBuf)); // Display "Hello World!", great
The problem is that when I try to encrypt larger data, I get an Exception on Encrypt Method "Value does not fall within the expected range."
For example the code :
int size = 59;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++)
{
sb.Append("a");
}
IBuffer buf = CryptographicBuffer.ConvertStringToBinary(sb.ToString(), BinaryStringEncoding.Utf16BE);
var encrypted = CryptographicEngine.Encrypt(encryptKey, buf, null);
... works for size = 58 but throws an enxception on Encrypt with size = 59.
The limit size depends on key size. This is the limit for a 1024 key size, but with 512 it's a bit smaller, and reversely.
My final buffer is an image, so obviously it will be much greater than my limit... and I don't really understand why the buffer is limited.
Did I do something wrong? Is there a problem in my code?
Have you an idea of how to encrypt large data with this method?
As you noted the size of the encryptable plaintext depends on the key size. When encrypting the message m is the base in modular exponentiation: me (mod n). If you have a message that is bigger or equal to n, it would be wrapped around to another message because of the modulus n.
If m > n and w ≡ m (mod n) then me (mod n) = we (mod n). So when you would decrypt the ciphertext, you wouldn't get the original message back. Because of this the library throws an error.
The solution is to use hybrid encryption. You first encrypt your data with a symmetric cipher like AES using a freshly generated random key. Now the random key for AES is at most 256 bit big, so it will fit into RSA with 1024 bit key (it doesn't fit into a 512 bit key because of padding). You encrypt the AES key with your public key and send the encrypted key along with the encrypted data.
On the other side you will use the private key to recover the random AES key and use it to decrypt the data.
SymmetricKeyAlgorithmProvider sp = SymmetricKeyAlgorithmProvider
.OpenAlgorithm(SymmetricAlgorithmNames.AesGcm);
The other recommended mode is SymmetricAlgorithmNames.AesCcm. GCM and CCM provide authentication (integrity) which CBC doesn't.
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.
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'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.