C# 中使用 MD5 和 SHA256 的 CryptoJS AES 加密未生成正确的值

CryptoJS AES encryption with MD5 and SHA256 in C# not generated proper value

我想使用 CryptoJS 和 C# 来加密密码。不幸的是,我的 C# 代码无法生成正确的值。这是我的代码

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
    }
}

上述 C# 代码的结果是 e969b60e87339625c32f805f17e6f993。它应该与 CryptoJS c3de82e9e8a28a4caded8c2ef0d49c80 相同。这里有什么问题?

在 CryptoJS 代码哈希(以 WordArrays 的形式)和字符串添加在几个地方。因此 WordArray 被隐式编码为 toString() 为小写字母的十六进制字符串。 C# 代码中缺少此内容。

在 C# 代码中,添加是通过 CombineByteArray() 完成的,其中散列作为 byte[] 在参数 first 中传递。因此这个参数必须先转换为小写字母的十六进制编码字符串,然后再进行UTF8编码,例如:

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;
}

其中 ByteArrayToString() 来自 here.

通过此更改,C# 代码给出与 CryptoJS 代码相同的结果。


我不太清楚 CryptoJS 代码的用途。通常明文和密钥是独立的,即不是从同一个密码派生的。
也许这应该实现自定义的基于密码的密钥派生功能。如果是这样,并且除非出于兼容性原因强制执行自定义实现,否则使用经过验证的算法(例如 Argon2 或 PBKDF2)会更安全。特别是缺少 salt/work 因素是不安全的。