3

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?

derodevil
  • 811
  • 1
  • 11
  • 37

1 Answers1

2

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.

Topaco
  • 40,594
  • 4
  • 35
  • 62