Getting incorrect decryption value using AesCryptoServiceProvider
.net, c#, cryptography, encryption
Solution
It is easy to make implementation mistakes with cryptographic primitives, people do it all the time, it's best to use a high level library if you can.
I have a snippet that I try to keep reviewed and up to date, that works pretty close to what you're doing. It also does authentication on the cipher text, which I would recommend if there is anyway an adversary could send chosen ciphertext to your decryption implementation, there are a lot of side channel attacks related to modifying the ciphertext.
However, the problem you're having does not have any thing to do with padding, if your ciphertext doesn't matchup to your key and iv, and you didn't authenticate your iv and ciphertext, you'll typically get a padding error (if this is bubbled up a client it's called a padding oracle). You need to change your main statement to:
string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
string inputValue = "Test";
string keyValue = valid128BitString;
byte[] byteValForString = Encoding.UTF8.GetBytes(inputValue);
EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
EncryptResult encyptedValue = new EncryptResult();
encyptedValue.IV = result.IV; //<--Very Important
encyptedValue.EncryptedMsg = result.EncryptedMsg;
string finalResult =Encoding.UTF8.GetString(Aes128Utility.DecryptData(encyptedValue, keyValue));
So you use the same IV to decrypt as you did to encrypt.
Problem
I have following code that uses `AesCryptoServiceProvider` for encrypting and decrypting. The `iv` and `key` used are same for both encryption and decryption. Still the decrypted value differ from the source string. - What need to be corrected to get the original value after decrypt? - This code is working when `inputValue = valid128BitString`. But when the `inputString = “Test”` I am getting the following exception `Padding is invalid and cannot be removed.`. How can we correct it? UPDATED QUESTION The following will do the trick based on @jbtule answer. ``` encyptedValue.IV = result.IV; ``` The `IV` value from encryption result changes. Suppose encryption is done in a separate process, how can we know the IV for decryption? Is there a way to make it constant or known? Answer: Your other option is pass a IV in to Encrypt and assign it before you begin your crypto transform, instead of letting aesProvider generate a random one for you. – @Scott Chamberlain ``` aesProvider.IV = Convert.FromBase64String("4uy34C9sqOC9rbV4GD8jrA=="); ``` Update: Refer How to apply padding for Base64. We can use `UTF8` for encoding the source input and result output. The key and IV may remain in `Base64`. Using Base64 for source input will cause issues with some values, for example, "MyTest" where length of string is not a multiple of 4 Relevant points: To decrypt data that was encrypted using one of the SymmetricAlgorithm classes, you must set the Key property and IV property to the same values that were used for encryption. SymmetricAlgorithm.IV Property: Information from the previous block is mixed into the process of encrypting the next block. Thus, the output of two identical plain text blocks is different. Because this technique uses the previous block to encrypt the next block, an initialization vector is needed to encrypt the first block of data. (As per SymmetricAlgorithm.IV Property MSDN article) The valid Key sizes are: 128, 192, 256 bits (as per How many characters to create a byte array for my AES method?) Main Program ``` class Program { static void Main(string[] args) { string valid128BitString = "AAECAwQFBgcICQoLDA0ODw=="; string inputValue = valid128BitString; string keyValue = valid128BitString; string iv = valid128BitString; byte[] byteValForString = Convert.FromBase64String(inputValue); EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue); EncryptResult encyptedValue = new EncryptResult(); encyptedValue.IV = iv; encyptedValue.EncryptedMsg = result.EncryptedMsg; string finalResult = Convert.ToBase64String(Aes128Utility.DecryptData(encyptedValue, keyValue)); Console.WriteLine(finalResult); if (String.Equals(inputValue, finalResult)) { Console.WriteLine("Match"); } else { Console.WriteLine("Differ"); } Console.ReadLine(); } } ``` AES Utility ``` public static class Aes128Utility { private static byte[] key; public static EncryptResult EncryptData(byte[] rawData, string strKey) { EncryptResult result = null; if (key == null) { if (!String.IsNullOrEmpty(strKey)) { key = Convert.FromBase64String((strKey)); result = Encrypt(rawData); } } else { result = Encrypt(rawData); } return result; } public static byte[] DecryptData(EncryptResult encryptResult, string strKey) { byte[] origData = null; if (key == null) { if (!String.IsNullOrEmpty(strKey)) { key = Convert.FromBase64String(strKey); origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV)); } } else { origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV)); } return origData; } private static EncryptResult Encrypt(byte[] rawData) { using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider()) { aesProvider.Key = key; aesProvider.Mode = CipherMode.CBC; aesProvider.Padding = PaddingMode.PKCS7; using (MemoryStream memStream = new MemoryStream()) { CryptoStream encStream = new CryptoStream(memStream, aesProvider.CreateEncryptor(), CryptoStreamMode.Write); encStream.Write(rawData, 0, rawData.Length); encStream.FlushFinalBlock(); EncryptResult encResult = new EncryptResult(); encResult.EncryptedMsg = Convert.ToBase64String(memStream.ToArray()); encResult.IV = Convert.ToBase64String(aesProvider.IV); return encResult; } } } private static byte[] Decrypt(byte[] encryptedMsg, byte[] iv) { using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider()) { aesProvider.Key = key; aesProvider.IV = iv; aesProvider.Mode = CipherMode.CBC; aesProvider.Padding = PaddingMode.PKCS7; using (MemoryStream memStream = new MemoryStream()) { CryptoStream decStream = new CryptoStream(memStream, aesProvider.CreateDecryptor(), CryptoStreamMode.Write); decStream.Write(encryptedMsg, 0, encryptedMsg.Length); decStream.FlushFinalBlock(); return memStream.ToArray(); } } } } ``` DTO Class ``` public class EncryptResult { public string EncryptedMsg { get; set; } public string IV { get; set; } } ``` References - How many characters to create a byte array for my AES method? - Specified key is not a valid size for this algorithm - Encryption with AES-256 and the Initialization Vector - Invalid length for a Base-64 char array - What's the difference between UTF8/UTF16 and Base64 in terms of encoding
Related problems
- Specified key is not a valid size for this algorithm
- Invalid length for a Base-64 char array
- What's the difference between UTF8/UTF16 and Base64 in terms of encoding
- Encryption with AES-256 and the Initialization Vector
- Encrypt and decrypt a string in C#?
- How many characters to create a byte array for my AES method?
- How to create byte[] with length 16 using FromBase64String