I am trying to encrypt and decrypt a file with the rijndael algorythm, but i have been getting the error "Length of the data to encrypt is invalid.". I am able to encrypt the file, but i can't decrypt it. This is my decryption function;
public static byte[] Decrypt(byte[] toEncryptArray)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("-key-");
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = rDel.CreateDecryptor();
return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
}
I honestly have no idea what i am doing wrong, as i can encrypt it perfectly fine. The file i am trying to decrypt is 11 kb.
You should be using the CryptoStream object, which will automatically call the correct ICryptoTransform.TransformFinalBlock and ICryptoTransform.TransformBlock methods.
You haven't posted the encryption code but check that the Padding mode the same (i.e. PaddingMode.PKCS7) and the initiation vector is set to the same string.
Related
I'm attempting to roundtrip crypting some data with the new .NET 6 AES one-shot APIs.
The following example works as expected:
using System.Security.Cryptography;
string key = "1234567890123456";
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
string dec = "Hello One-Shot API!";
byte[] decBytes = Encoding.ASCII.GetBytes(dec);
// Encrypt
using Aes aes = Aes.Create();
byte[] encBytes = aes.EncryptCbc(decBytes, keyBytes);
// Decrypt again
byte[] recBytes = aes.DecryptCbc(encBytes, keyBytes);
string rec = Encoding.ASCII.GetString(recBytes);
Debug.Assert(rec == dec);
But if I create a new Aes instance to call DecryptCbc on, it throws a System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. exception:
...
// Encrypt
using Aes aes = Aes.Create();
byte[] encBytes = aes.EncryptCbc(decBytes, keyBytes);
// Decrypt again - this time with new instance
using Aes aes2 = Aes.Create();
byte[] recBytes = aes2.DecryptCbc(encBytes, keyBytes); // <- throws
...
Am I missing something here? Is there state set up in an Aes instance when calling EncryptCbc that doesn't exist when only using it for decryption? Do I have to keep the Aes instance around, and then how would I use it for decryption initially if I haven't encrypted with it before?
As mentioned in the comments, I mistook the IV parameter for the key, as I was porting old code not using any IV.
Once setting the key in the Aes.Key property, and using an empty IV (or even better, fixing the old code and using a proper IV), it worked as expected with every new instance.
I am using this function to encrypt data in my UWP project:
public string Encrypt(string text, string key)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
byte[] sessionKey = Encoding.UTF8.GetBytes(key);
Aes myAes = Aes.Create();
myAes.Mode = CipherMode.ECB;
myAes.KeySize = 128;
myAes.Key = sessionKey;
myAes.Padding = PaddingMode.PKCS7;
ICryptoTransform encryptor = myAes.CreateEncryptor();
buffer = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
return Convert.ToBase64String (buffer);
}
But upon decrypting the data returned from Encrypt() , I get a different result (not equal to the text parameter of Encrypt() ). I am using the following code:
public string Decrypt(string text, string key)
{
byte[] buffer = Convert.FromBase64String(text);
byte[] sessionKey = Encoding.UTF8.GetBytes(key);
Aes myAes = Aes.Create();
myAes.Mode = CipherMode.ECB;
myAes.KeySize = 128;
myAes.Key = sessionKey;
myAes.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = myAes.CreateDecryptor();
buffer = decryptor.TransformFinalBlock(buffer, 0, buffer.Length);
return Convert.ToBase64String(buffer);
}
I am using the same key for both encryption and decryption
UPDATE:
text parameter passed to Encrypt() : 450131283::0300DC98050044C406000100040052C40100626B02007E810900660F
Return text from Encrypt():
"lzkPu35Hq7j52IiMWRYSS6j7Vg84abVmhXmNpSxHShJDTbOqkZRFtsPZkEzTsjgRT4MzRHCQUS6MCiq1e5JCune4bZZi1nxxwHtEjZLKZ9E="
the same (above) value I pass to the Decrypt() method and I get the following:
"NDUwMTMxMjgzOjowMzAwREM5ODA1MDA0NEM0MDYwMDAxMDAwNDAwNTJDNDAxMDA2MjZCMDIwMDdFODEwOTAwNjYwRg=="
The problem is what you're doing with the end of the decryption:
return Convert.ToBase64String(buffer);
You actually want to convert the decrypted binary data back into a string in a way that mirrors the original way you converted it from a string into plaintext binary data, so you want:
return Encoding.UTF8.GetString(buffer);
This sort of problem is usually best addressed by looking at every step in the transformation chain each direction, and make sure that they're balanced. So it should look like this:
Text
(Encode with UTF-8)
Non-encrypted binary data
(Encrypt)
Encrypted binary data
(Convert to base64)
Encrypted data as base64 text
(Store or whatever...)
Encrypted data as base64 text
(Convert from base64)
Encrypted binary data
(Decrypt)
Non-encrypted binary data
(Decode with UTF-8)
Text
Where I've got "decode with UTF-8" you've got "Convert to base64" so the decoding steps don't match the encoding steps.
I am building a iPhone app which uses a c# web service. My iPhone app takes in some data and encrypts it and passes it to the web service. How do I decrypt the data in C#?
My iPhone app contains the following code:
NSString *pString = #"Some string to be encoded";
NSString *key = #"My encryption key";
NSData *pData = [pString dataUsingEncoding:NSUTF8StringEncoding];
pData = [pData AES256EncryptWithKey:key];
NSString *pID = [pData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];
EDIT: The data is already stored in the web service so I can't readily change the encryption approach. The C# application is not on the server so there is no possibility of compromising the key.
I have tried the following C# code to decrypt the data:
static string DecryptString(string encryptedText, string key)
{
byte[] encryptedString = Convert.FromBase64String(encryptedText);
byte[] encryptionKey = Encoding.UTF8.GetBytes(key.Substring(0, 32));
using (var provider = new AesCryptoServiceProvider())
{
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
{
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
}
}
}
However, I get the following exception:
System.Security.Cryptography.CryptographicException was unhandled
HResult=-2146233296 Message=Padding is invalid and cannot be
removed.
The encryptedText received by DecryptString is 80 bytes in length.
The sample ObjC code uses by default CBC modem, PKCS#7 padding and a default iv of 16 0x00 bytes.
The C# also uses CBC mode and PKCS#7 padding. The decryption code expects a 16-byte iv pre-pended to the encrypted data and that does not exist.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
This needs to be changed so that iv is set to an array of 16 0x00 bytes and the ms.Read(iv, 0, 16) statement needs to be deleted so the decrypt function gets all of the encrypted data.
Notes:
Using a devault anything in encryption is a bad idea, always provide the correect length data.
Authentication of the encrypted data needs should be added so that it can be determined if there an incorrect key or the data has been tampered with.
There really should be a version number and a random IV used and prepended to the encrypted so you should really consider correcting this. This demonstrates why a version number generally needs to be provided and used.
RNCryptor covers the above issues.
The handling of the encryption key also needs to be considered so that is is as secure as necessary.
You need to first decode the base-64 encoded string to a byte[] - see Convert.FromBase64String(). Then you need to use the Aes class to decrypt it - there's an example on its documentation page.
I am getting length of the string wrong after using the following Decryption Method.
public static string DecryptRJ256(string prm_key, string prm_iv, string prm_text_to_decrypt) {
string sEncryptedString = prm_text_to_decrypt;
RijndaelManaged myRijndael = new RijndaelManaged();
myRijndael.Padding = PaddingMode.Zeros;
myRijndael.Mode = CipherMode.CBC;
myRijndael.KeySize = 256;
myRijndael.BlockSize = 256;
byte[] key = Encoding.ASCII.GetBytes(prm_key);
byte[] IV = Encoding.ASCII.GetBytes(prm_iv);
ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV);
byte[] sEncrypted = Convert.FromBase64String(sEncryptedString);
byte[] fromEncrypt = new byte[sEncrypted.Length];
MemoryStream msDecrypt = new MemoryStream(sEncrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
For example:
string ID = "yUFYhclPyPubnhMZ+SHJb1wrt44pao3B82jdbL1ierM=";
string finalID = DecryptRJ256(sKy, sIV, ID);
Response.Write(finalID); \\200905410 (**this is correct**)
Response.Write(finalID.Length); \\32 (**this should be 9**)
What am I doing wrong?
You are using zero padding. This pads the message with zero bytes until it reaches the block size (32 bytes in your case). Since zero padding is ambiguous (can't distinguish between an input that ended with zero bytes and the padding), .net doesn't remove it automatically.
So you have two choices:
Use PKCS7 padding for both encryption and decryption (that's what I recommend)
Manually strip all terminal zero bytes from the decrypted plaintext.
Your crypto isn't good either:
Keys and IVs should be binary, not ASCII (use base64 encoding here)
Using ASCII on the plaintext silently corrupts unicode characters - Use utf-8 instead
You need a new random IV for each encryption call and need to read it back during decryption
You should add a MAC, else active attacks (such as padding oracles) can often break it.
Use TransformFinalBlock instead of those memory streams.
Why use Rijndael256 over AES?
When I compiled this with symmetric decryptor object with the current Key, that is without key and IV, I get this as finalID.
???hV?9-2O?o?????}yl?????N?W
exactly 32 characters.
Refining the key and IV would help. I am not sure, but hope this might help.
This is the first time that I have worked with AES encryption, and I am somewhat confused. I am encrypting some sensitive data (no passwords just names, address, id number etc). I found a nice example of how to implement AES into my application. I understand that I supply the Password, Salt, Hash Algorithm, and Password Iterations to make the Key. This then creates the cipher text. Couple of questions I have is why is the KeySize dived by 8? If I wanted someone else to decrypt this what do I need to give them? Should the password, salt and IV be different every time I encrypt something new, if that is the case how to I ensure that the other person I want to use this information is always aware of that?
public static string Encrypt(string PlainText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
try
{
byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
cryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
cryptoStream.Close();
MemStream.Dispose();
cryptoStream.Dispose();
Encryptor.Dispose();
return Convert.ToBase64String(CipherTextBytes);
}
catch (Exception ex)
{
throw ex;
}
}
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
The keysize is divided by 8 because it's typically specified as a number of bits, but is being stored in a byte array.
The values that you keep the same are subject to the design of your program. In practice, the IV should change every time, but should you choose, you could keep the key and salt constant.
I know that the IV is included in the output of the stream, so you don't have to include it explicitly for decryption (it will be read from the stream). You shouldn't have to include the salt either for a similar reason. I believe that the only value you'd need to decrypt the cyphertext is the key. I may not be correct about this. You can always write a decryptor and see what you need to pass to make it work.
KeySize is in bits. And as we know on any normal machine 1 byte = 8 bits, hence division.
And what you need to give someone to be able to decrypt the data? Key and IV (Initialization Vector) will be enough.
That said - how do you usually do this? You send the AES encrypted data along with Key and IV encrypted using some asymetric encryption algorithm. This gives you higher level of protection - only someone with private key will be able to read Key and IV and use them do decrypt the most interesting data. You use generated Key and IV of some significant length to make yourself safe.
Why to do this this way? Because asymetric encryption is slow (very slow and for a good reason - to be harder to break), but symetric encryption (like AES) gives you high security if used with long key and is at the same time pretty fast. Combining both you get performance and security at high level.