I want to encrypt password using both in CryptoJS and C#. Unfortunately, my C# code fails to generate the proper value. This is my code
internal static byte[] ComputeSha256(this byte[] value)
{
using (SHA256 sha256Hash = SHA256.Create())
return sha256Hash.ComputeHash(value);
}
internal static byte[] ComputeSha256(this string value) => ComputeSha256(Encoding.UTF8.GetBytes(value));
internal static byte[] ComputeMD5(this byte[] value)
{
using (MD5 md5 = MD5.Create())
return md5.ComputeHash(value);
}
internal static byte[] ComputeMD5(this string value) => ComputeMD5(Encoding.UTF8.GetBytes(value));
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
internal static string EncryptPassword()
{
using (AesManaged aes = new AesManaged())
{
//CLIENT SIDE PASSWORD HASH
/*
var password = '12345';
var passwordMd5 = CryptoJS.MD5(password);
var passwordKey = CryptoJS.SHA256(CryptoJS.SHA256(passwordMd5 + '12345678') + '01234567890123456');
var encryptedPassword = CryptoJS.AES.encrypt(passwordMd5, passwordKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });
encryptedPassword = CryptoJS.enc.Base64.parse(encryptedPassword.toString()).toString(CryptoJS.enc.Hex);
//encryptedPassword result is c3de82e9e8a28a4caded8c2ef0d49c80
*/
var y1 = Encoding.UTF8.GetBytes("12345678");
var y2 = Encoding.UTF8.GetBytes("01234567890123456");
var password = "12345";
var passwordMd5 = ComputeMD5(password);
var xkey = CombineByteArray(ComputeSha256(CombineByteArray(passwordMd5, y1)), y2);
var passwordKey = ComputeSha256(xkey);
aes.Key = passwordKey;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
ICryptoTransform crypt = aes.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(passwordMd5, 0, passwordMd5.Length);
var encryptedPassword = BitConverter.ToString(cipher).Replace("-", "").ToLower();
return encryptedPassword; //e969b60e87339625c32f805f17e6f993
}
}
The result of the C# code above is e969b60e87339625c32f805f17e6f993. It should be the same with CryptoJS c3de82e9e8a28a4caded8c2ef0d49c80. What is wrong here?
In the CryptoJS code hashes (in the form of WordArrays) and strings are added in several places. Thereby the WordArray is implicitly encoded with toString() into a hex string with lowercase letters. This is missing in the C# code.
In the C# code the addition is done with CombineByteArray(), where the hash is passed in the parameter first as byte[]. Therefore this parameter must first be converted to a hex encoded string with lowercase letters and then UTF8 encoded, e.g.:
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
// Hex encode (with lowercase letters) and Utf8 encode
string hex = ByteArrayToString(first).ToLower();
first = Encoding.UTF8.GetBytes(hex);
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
where ByteArrayToString() is from here.
With this change, the C# code gives the same result as the CryptoJS code.
I am not quite clear about the purpose of the CryptoJS code. Usually plaintext and key are independent, i.e. are not derived from the same password.
Perhaps this is supposed to implement a custom password-based key derivation function. If so, and unless a custom implementation is mandatory for compatibility reasons, it is more secure to use a proven algorithm such as Argon2 or PBKDF2. In particular, the lack of a salt/work factor is insecure.