AesCryptoServiceProvider - "Padding is invalid and cannot be removed" + c# - c#

I have gone through the questions with the same issue but are of no help. I checked my code and it's same as they suggested.
//Create the hash value from the array of bytes.
byte[] KeyHashValue = SHhash.ComputeHash(Encoding.UTF8.GetBytes(key.Length > 0? key : Password));
aesProvider.BlockSize = 128;
aesProvider.KeySize = 256;
aesProvider.IV = App.iv;
aesProvider.Key = KeyHashValue;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
//aesProvider.Padding = PaddingMode.None;
// Convert Base64 strings to byte array
byte[] src = System.Convert.FromBase64String(encrypted);
// decryption
using (ICryptoTransform decrypt = aesProvider.CreateDecryptor())
{
try
{
byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length); ---> Getting crash at this line.
plain = Encoding.Unicode.GetString(dest);
}
catch (Exception exception)
{
System.Windows.MessageBox.Show("In catch of decrypt : " + exception.Message);
}
}
Can anyone suggest me the required changes? I have also tried to change the padding to None but that didn't work too.
UPDATE :
I am using email id as Key here. If I use the small letters it works but if I use any capital in email id, it gives this error.

Related

Get a C# byte array equal to a php hex string

So I have this piece of php code that I'm not allowed to modify for now, mainly because it's old and works properly.
Warning! Very bad code overal. the IV is not being randomized neither stored with the output. I'm not asking this because I want to,
I'm asking because I need to. I'm also planning on refactoring when I get this working and completing my C# code with actually reliable cyphering code.
function encrypt($string)
{
$output = false;
$encrypt_method = "AES-256-CBC";
$param1 = 'ASasd564D564aAS64ads564dsfg54er8G74s54hjds346gf445gkG7';
$param2 = '654dsfg54er8ASG74sdfg54hjdas346gf34kjdDJF56hfs2345gkFG';
$ky = hash('sha256', $param1); // hash
$iv = substr(hash('sha256', $param2), 0, 16);
$output = openssl_encrypt($string, $encrypt_method, $ky, 0, $iv);
$output = base64_encode($output);
return $output;
}
I want to do the same in C# because I'm getting an entity with all its fields encrypted with that code.
I want to be able to encrypt that data so I can query my entity list whithout having to decrypt all the entities. And I want to decrypt some properties of the filtered entities so they can actually be useful.
Now, for that matter I created a CryptoHelper that will do this, except it doesn't.
I try to calculate the Key and IV in the constructor:
public readonly byte[] Key;
public readonly byte[] IV;
public CryptoHelper()
{
Key = GetByteArraySha256Hash("ASasd564D564aAS64ads564dsfg54er8G74s54hjds346gf445gkG7", false);
IV = GetByteArraySha256Hash("654dsfg54er8ASG74sdfg54hjdas346gf34kjdDJF56hfs2345gkFG", true);
}
private byte[] GetByteArraySha256Hash(string source, bool salt)
{
byte[] result;
try
{
using (SHA256 sha256Hash = SHA256.Create())
{
result = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(source));
}
}
catch (Exception)
{
throw;
}
if (salt)
{
return result.Take(16).ToArray();
}
return result;
}
And then use a Encrypt and Decrypt methods that are working pretty well when I test them with a test string. The only problem is that the string have some padding at the end, but it's kind of a minor problem considering that any string encrypted with the php method results in gibberish.
private string Encrypt(string source)
{
try
{
string result = "";
using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros })
{
byte[] sourceByteArray = Encoding.UTF8.GetBytes(source);
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
byte[] encriptedSource = encryptor.TransformFinalBlock(sourceByteArray, 0, sourceByteArray.Length);
result = Convert.ToBase64String(encriptedSource);
result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
}
}
return result;
}
catch (Exception ex)
{
throw;
}
}
private string Decrypt(string source)
{
try
{
string result = "";
//Double Base64 conversion, as it's done in the php code.
byte[] sourceByte = Convert.FromBase64String(source);
byte[] sourceFreeOfBase64 = Convert.FromBase64String(Encoding.UTF8.GetString(sourceByte));
byte[] resultByte;
int decryptedByteCount = 0;
using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros })
{
using (ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV))
{
using (MemoryStream memoryStream = new MemoryStream(sourceFreeOfBase64))
{
using (CryptoStream cs = new CryptoStream(memoryStream, AESDecrypt, CryptoStreamMode.Read))
{
resultByte = new byte[sourceFreeOfBase64.Length];
decryptedByteCount = cs.Read(resultByte, 0, resultByte.Length);
}
}
}
//This returns the encoded string with a set of "\0" at the end.
result = Encoding.UTF8.GetString(resultByte);
result = result.Replace("\0", "");
}
return result;
}
catch (Exception ex)
{
throw;
}
}
I'm pretty sure that the main problem here lies in the php line $iv = substr(hash('sha256', $param2), 0, 16);. I checked the results of both hash functions in php and C# and are exactly the same.
From what I've been reading php treats strings as byte arrays (correct me if I'm wrong) so a 16 char string should be enough to get a 16 byte array and a 128 block. But in C#, when I get the 16 byte array and convert it to a string I get a 32 char string that is the same as if I did $iv = substr(hash('sha256', $param2), 0, 32);.
So my question is, how do I get the same byte array result in C# that I get in this line $iv = substr(hash('sha256', $param2), 0, 16); of php? Is this even possible?
The hash function will return the same number of bytes whatever the input, so I suspect it is a difference in how you convert the resulting byte[] back to a string in C# compared to the PHP implementation.
The PHP docs say that the hash function output the result in lower case hexits. This is absolutely not the same as the UTF8 encoding that you are returning.
There isn't a built in framework way to do this, but check out this SO question for several different methods.
Also worth noting is that you do not specify the Padding value in your C# code. AES-CBC is a block cipher and will need to use some padding scheme. You may well get a padding exception. I think that it will need Zero padding (docs)
aes.Padding = PaddingMode.Zeros
but I'm not 100%
Well, I managed to solve this in a not so bad manner.
Following #ste-fu advice I tried to get rid of every piece of encoding that I could find.
But I still wasn't anywhere close to getting the Key and IV right. So I did some testing with php. I made a var_dump of the IV and got a neat 16 length array with bytes shown as integers.
var_dump result array starts allways in [1]. Be advised.
$iv = substr(hash('sha256', $param2), 0, 16);
$byte_array = unpack('C*', $iv);
var_dump($byte_array);
That peaked my interest, thinking that if I had the hex string right I should be able to convert each char in the string to it's equivalent byte. Lo and behold, I made this function in C#:
private byte[] StringToByteArray(string hex)
{
IList<byte> resultList = new List<byte>();
foreach (char c in hex)
{
resultList.Add(Convert.ToByte(c));
}
return resultList.ToArray();
}
And this worked very well for the IV. Now I just had to do the same thing for the key. And so I did, just to find that I had a 64 length byte array. That's weird, but ok. More testing in php.
Since it does make sense that the php Key behaves the same as the IV I didn't get how the openssl encryption functions allowed a 64 length Key. So I tryed to encrypt and decrypt the same data with a Key made from the first 32 chars. $ky = substr(hash('sha256', $param1), 0, 32);
And it gave me the same result as with the full Key. So, my educated guess is that openssl just takes the bytes necesary for the encoding to work. In fact it will take anything since I tested with substrings of 1, 16, 20, 32, 33 and 50 length. If the length of the string is bigger than 32 the function itself will cut it.
Anyway, i just had to get the first 32 chars of the Key hex and use my new function to convert them into a byte array and I got my Key.
So, the main C# code right now looks like this:
public CryptoHelper(string keyFilePath, string ivFilePath)
{
//Reading bytes from txt file encoded in UTF8.
byte[] key = File.ReadAllBytes(keyFilePath);
byte[] iv = File.ReadAllBytes(ivFilePath);
IV = StringToByteArray(GetStringHexSha256Hash(iv).Substring(0, 16));
Key = StringToByteArray(GetStringHexSha256Hash(key).Substring(0, 32));
//Tests
var st = Encrypt("abcdefg");
var en = Decrypt(st);
}
//Convert each char into a byte
private byte[] StringToByteArray(string hex)
{
IList<byte> resultList = new List<byte>();
foreach (char c in hex)
{
resultList.Add(Convert.ToByte(c));
}
return resultList.ToArray();
}
private string GetStringHexSha256Hash(byte[] source)
{
string result = "";
try
{
using (SHA256 sha256Hash = SHA256.Create("SHA256"))
{
//Get rid of Encoding!
byte[] hashedBytes = sha256Hash.ComputeHash(source);
for (int i = 0; i < hashedBytes.Length; i++)
{
result = string.Format("{0}{1}",
result,
hashedBytes[i].ToString("x2"));
}
}
}
catch (Exception)
{
throw;
}
return result;
}
private string Encrypt(string source)
{
try
{
string result = "";
using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
{
byte[] sourceByteArray = Encoding.UTF8.GetBytes(source);
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
byte[] encriptedSource = encryptor.TransformFinalBlock(sourceByteArray, 0, sourceByteArray.Length);
result = Convert.ToBase64String(encriptedSource);
//Nothing to see here, move along.
result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
}
}
return result;
}
catch (Exception ex)
{
throw;
}
}
private string Decrypt(string source)
{
try
{
string result = "";
byte[] sourceByte = Convert.FromBase64String(source);
byte[] sourceFreeOfBase64 = Convert.FromBase64String(Encoding.UTF8.GetString(sourceByte));
byte[] resultByte;
int decryptedByteCount = 0;
using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
{
using (ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV))
{
using (MemoryStream memoryStream = new MemoryStream(sourceFreeOfBase64))
{
using (CryptoStream cs = new CryptoStream(memoryStream, AESDecrypt, CryptoStreamMode.Read))
{
resultByte = new byte[sourceFreeOfBase64.Length];
//Now that everything works as expected I actually get the number of bytes decrypted!
decryptedByteCount = cs.Read(resultByte, 0, resultByte.Length);
}
}
}
//Nothing to see here, move along.
result = Encoding.UTF8.GetString(resultByte);
//Use that byte count to get the actual data and discard the padding.
result = result.Substring(0, decryptedByteCount);
}
return result;
}
catch (Exception ex)
{
throw;
}
}
I still need to clean all the code from my class from all the testing I did, but this is all that's needed to make it work.
I hope this helps anybody with the same problem that I faced.
Cheers.

AESManaged returns different encrypted value for same key/iv combination

I found this article online and implemented a modified version of it.
public static byte[] Encrypt(byte[] input, byte[] iv)
{
var aes = new AesManaged();
aes.Key = StringToByteArray("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
aes.IV = StringToByteArray("00010001000000000000000000000000");
aes.KeySize = 128;
var encryptor = aes.CreateEncryptor();
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(input, 0, input.Length);
cs.Close();
}
return ms.ToArray();
}
}
public static byte[] StringToByteArray(string hex)
{
var NumberChars = hex.Length;
var bytes = new byte[NumberChars / 2];
for (var i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
Now the question I have is, here I am providing same IV and Key (obviously just for testing, in production I'm changing the IV each time I encrypt), but it's returning different encrypted bytes each time I encrypt the same input.
I looked up some posts and they said the output is supposed to be same for specific key/iv combination. Am I missing something here?
EDIT:
[TestMethod]
public void Encryption_returns_same_value_for_same_key_and_iv()
{
const string input = "my input";
var bytes = Encoding.UTF32.GetBytes(input);
var result = EncryptionManager.Encrypt(bytes, bytes);
var result2 = EncryptionManager.Encrypt(bytes, bytes);
Assert.AreEqual(result, result2);
}
This is how i'm calling the encrypt method
So after some discussion, the problem was actually part of the code which was not shown here. Indeed, the original code above always gave the same results and the Unit test should have passed (with additionally using SequenceEqual on the assertion). However, the aes.KeySize was changed in the code (by some colleagues) after setting the key, like this:
aes.Key = StringToByteArray("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
aes.IV = StringToByteArray("00010001000000000000000000000000");
aes.KeySize = 128;
However, as we found out by outputting the used key after setting the KeySize property using
Console.WriteLine("Used Key for Encryption: " + BitConverter.ToString(aes.Key));
the key changes to a random key after you modify the KeySize. That's why we kept getting different results. Sample outputs for calling the function with the same input vector:
Used Key for Encryption: C7-35-58-42-3A-2A-79-DE-0D-09-78-20-34-90-1F-EC
Ciphertext: E4-AA-A3-3B-01-CF-F0-C1-07-9A-0B-73-3E-70-C9-8A
Used Key for Encryption: 8A-95-E7-26-60-F9-CE-66-BA-A4-DE-D2-FA-70-AC-DE
Ciphertext: C5-E7-D3-32-38-21-54-25-86-61-70-CB-94-46-A6-37
Used Key for Encryption: A4-D7-01-8F-35-2B-7F-2D-E6-0A-A9-7F-95-42-71-D6
Ciphertext: F1-B2-75-64-D1-90-75-32-0D-CB-D9-AE-11-AE-DB-DD
The problem is solved by first setting the KeySize and then setting the Key property itself.

How to fix invalid key size when decrypting data in C# that was encrypted in php

I am trying to solve an encryption issue I am having between php and c#.
I have encrypted data using the following php and openssl operation.
$encrypt_method = "AES-256-CBC";
$secret_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$secret_iv = 'XXXXXXXXXXXXXXXX';
$key = hash ('sha256', $secret_key);
$iv = substr (hash ('sha256', $secret_iv), 0, 16);
$output = openssl_encrypt ($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode ($output);
I have tried a couple of methods in C# to decrypt but this is what I am trying now.
public string Encrypt_Decrypt(string action, string value) {
string secretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
string secretIV = "XXXXXXXXXXXXXXXX";
string key = Hash(secretKey);
string iv = Hash(secretIV).Substring(0,16);
string retValue = "";
if (action == "encrypt") {
retValue = EncryptString(value, key, iv);
}
else if (action == "decrypt") {
retValue = DecryptString(value, key, iv);
}
}
// Hash to match php hash function
public static string Hash(string unhashedString) {
return BitConverter.ToString(new SHA256CryptoServiceProvider().ComputeHash(Encoding.Default.GetBytes(unhashedString))).Replace("-", String.Empty).ToLower();
}
public static string DecryptString(string cipherData, string keyString, string ivString) {
byte[] key = Encoding.UTF8.GetBytes(keyString);
Console.WriteLine(key.Length);
byte[] iv = Encoding.UTF8.GetBytes(ivString);
Console.WriteLine(iv.Length);
byte[] cipherCrypt = Convert.FromBase64String(cipherData);
for (int i = 0; i < cipherCrypt.Length; i++) {
Console.Write(cipherCrypt[i] + " ");
}
try {
RijndaelManaged crypto = new RijndaelManaged();
crypto.Key = key;
crypto.IV = iv;
crypto.Mode = CipherMode.CBC;
crypto.KeySize = 256;
crypto.BlockSize = 128;
crypto.Padding = PaddingMode.None;
ICryptoTransform decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV);
using (MemoryStream memStream = new MemoryStream(cipherCrypt)) {
using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read)) {
using (StreamReader streamReader = new StreamReader(cryptoStream)) {
return streamReader.ReadToEnd();
}
}
}
}
catch (CryptographicException e) {
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
return null;
}
}
I have tried a couple different encoding types when getting the byte[] for the operation.
I keep getting the following error:
Specified key is not a valid size for this algorithm.
Not sure what I am missing. Any help is appreciated.
Also, I already read through this and tried what the solution suggestion recommended. I got the same resulting error.
UPDATE - 01
I have updated the code here to reflect the code I have changed.
The key length is 32,
The iv length is 16,
The data coming in at "cipherData" is length 32,
When "cipherData" goes through "FromBase64String(cipherData)" it comes out as a 24 byte array. This is causing an issue for the decryptor which wants a 32 byte array.
There are obviously problems with the key size. The code between PHP and C# seem to match. The problem seems to be that the code is wrong in both cases.
Let's see how long the key actually is:
Start with a 32 byte key (non-encoded).
Hash the key with SHA-256: 32 bytes (non-encoded).
Encode to hex (integrated into PHP's hash() function by default): 64 bytes.
AES only supports the following key sizes: 16, 24 and 32 bytes. openssl_encrypt() will only use the first 32 bytes of the hex key silently. So, you need to use the first 32 bytes in C#.
Note that openssl_encrypt() takes an options argument which denotes that the output is Base64 when OPENSSL_RAW_DATA is not set. It means that the PHP output was encoded twice with Base64. So you need to decode it twice in C#.

Arithmetic signs in encrypted value cannot decrypt on live server in c#

I am working in a MVC3 Project. Encryption and decryption method are working fine in locally, but when I put on live some values are not hitting. I found that the arithmetic symbols like +,/ cause the problem. How can I resolve this?
My code is given below:
RijndaelManaged aese = new RijndaelManaged();
public string Encrypt(string stringToEncrypt)
{
try
{
aese.Mode = CipherMode.CBC;
aese.Padding = PaddingMode.PKCS7;
aese.BlockSize = 128;
aese.KeySize = 128;
aese.Key = Encoding.UTF8.GetBytes("bsGCslxDSrPTesVG");
aese.IV = Encoding.UTF8.GetBytes("IkLNSuWfZaQdPQCS");
byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);
ICryptoTransform crypto = aese.CreateEncryptor();
byte[] cipherText = crypto.TransformFinalBlock(inputByteArray, 0, inputByteArray.Length);
return (Convert.ToBase64String(cipherText));
}
catch
{
return "Encryption Error";
}
}
public string Decrypt(string stringToDecrypt)
{
byte[] inputByteArray = new byte[stringToDecrypt.Length + 1];
try
{
aese.Mode = CipherMode.CBC;
aese.Padding = PaddingMode.PKCS7;
aese.BlockSize = 128;
aese.KeySize = 128;
aese.Key = Encoding.UTF8.GetBytes("bsGCslxDSrPTesVG");
aese.IV = Encoding.UTF8.GetBytes("IkLNSuWfZaQdPQCS");
inputByteArray = Convert.FromBase64String(stringToDecrypt);
ICryptoTransform decrypto = aese.CreateDecryptor();
byte[] plainText = aese.CreateDecryptor().TransformFinalBlock(inputByteArray, 0, inputByteArray.Length);
return Encoding.UTF8.GetString(plainText);
}
catch
{
return "Dycription Error";
}
}
Given you're working with a web project, I'm going to guess that you're passing Base64-encoded cipher-text as a URL or similar. Because Base64 data contains characters that are interpreted differently when treated as a URL (e.g. a + in a URL is interpreted as space), your data will be corrupted if you include Base64-encoded data in a URL verbatim.
If you intend to pass Base64 data in this way, you must correctly URL-encode it (e.g. using System.Web.HttpUtility.UrlEncode()) before including it in the URL.
There is a URL-safe version of Base64, which uses - (minus) for value 62 and _ (underline) for value 63. See RFC 4648, section 5 for details.

Decrypt TripleDES "Bad Data"

I'm new to encryption/decryption. I'm trying to decrypt an input string that is encrypted and comes out to 44 characters.
This is what I have so far but I keep getting "bad data" when it attempts to execute the "TransformFinalBlock" function.
public static String Decrypt(String input)
{
try{
byte[] inputArray = Convert.FromBase64String(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.KeySize = 128;
tripleDES.Key = UTF8Encoding.UTF8.GetBytes("0123456789ABCDEF");
tripleDES.IV = UTF8Encoding.UTF8.GetBytes("ABCDEFGH");
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform transform = tripleDES.CreateDecryptor();
byte[] resultArray = transform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return UTF8Encoding.UTF8.GetString(resultArray);
}
catch(Exception except){
Debug.WriteLine(except + "\n\n" + except.StackTrace);
return null;
}
}
If you use an IV, then you should use CipherMode.CBC. ECB does not use any IV.
In addition, your data is not padded at all, it contains exactly 32 bytes. To test decryption, it is common to try without padding first. That way you can determine by eye which padding is used by looking at the resulting plaintext.
The plain data is too corny to print here, so I won't.
I had a very similar issue and i fixed it by changing the PaddingMode to None
My CipherMode is ECB (Electronic code book).

Categories

Resources