You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让C#加密结果可被Node.js的CryptoJS.AES.decrypt解密?

C#实现与CryptoJS AES加密等效的问题及解决

问题描述

需要在C#中实现与Node.js以下代码完全等效的AES加密功能:

let encrypted = CryptoJS.AES.encrypt(JSON.stringify(text), passPhrase).toString()

确保C#加密后的内容能通过Node.js的以下代码解密:

var decrypted = CryptoJS.AES.decrypt(encrypted, passPhrase);

已编写的C#密钥派生方法:

public static byte[] DeriveKeyAndIVFromPassword(string password, int derivedBytesLength)
{
    using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, derivedBytesLength))
    {
        return rfc2898.GetBytes(derivedBytesLength);
    }
}

主加密方法:

public static string EncryptAES(string plainText, string passphrase)
{
    try
    {
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.Mode = CipherMode.CBC;  // 使用CBC模式
            aesAlg.Padding = PaddingMode.PKCS7;  // 使用PKCS7填充
            aesAlg.KeySize = 256;  // 使用AES-256

            byte[] derivedKeyAndIV = DeriveKeyAndIVFromPassword(passphrase, aesAlg.KeySize / 8 + aesAlg.BlockSize / 8);

            byte[] key = derivedKeyAndIV.Take(aesAlg.KeySize / 8).ToArray();
            byte[] iv = derivedKeyAndIV.Skip(aesAlg.KeySize / 8).Take(aesAlg.BlockSize / 8).ToArray();

            aesAlg.Key = key;

            using (var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, iv))
            {
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    msEncrypt.Write(iv, 0, iv.Length);

                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                    }
                    return Convert.ToBase64String(msEncrypt.ToArray());
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: " + ex.Message);
        return null;
    }
}

虽然设置了CBC模式、PKCS7填充和256位密钥,但C#生成的加密内容无法被CryptoJS解密,请问问题出在哪里?


问题分析及修正

你的代码主要有3个核心问题,导致和CryptoJS的默认行为不匹配:

1. 密钥派生算法不匹配

CryptoJS的AES.encrypt默认使用OpenSSL的EVP_BytesToKey算法生成密钥和IV,而非你用的PBKDF2(Rfc2898DeriveBytes)。只有手动指定key参数时,CryptoJS才会跳过这个派生过程。

2. 加密结果的格式不匹配

CryptoJS生成的加密字符串是特定的OpenSSL格式:Salted__ + 8字节随机salt + 密文,而你的C#代码直接把IV写在密文前面,格式完全不同。

3. 文本编码不一致

C#的StreamWriter默认使用UTF-16编码写入明文,而CryptoJS默认用UTF-8处理明文,这会导致明文内容在加密前就不一致。


修正后的C#代码

第一步:实现EVP_BytesToKey算法(匹配CryptoJS默认行为)

public static (byte[] Key, byte[] IV) EvpBytesToKey(string password, int keySize, int blockSize)
{
    var passwordBytes = Encoding.UTF8.GetBytes(password);
    var key = new byte[keySize / 8];
    var iv = new byte[blockSize / 8];
    var temp = new byte[0];
    var hash = new byte[0];

    using (var md5 = MD5.Create())
    {
        while (key.Length + iv.Length > temp.Length)
        {
            hash = md5.ComputeHash(temp.Concat(passwordBytes).ToArray());
            temp = temp.Concat(hash).ToArray();
        }

        Array.Copy(temp, key, key.Length);
        Array.Copy(temp, key.Length, iv, 0, iv.Length);
    }

    return (key, iv);
}

第二步:修正加密方法,匹配CryptoJS的输出格式

public static string EncryptAES(string plainText, string passphrase)
{
    try
    {
        using (var aes = Aes.Create())
        {
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            aes.KeySize = 256;
            int blockSize = aes.BlockSize;

            // 生成随机8字节salt(CryptoJS默认行为)
            var salt = new byte[8];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(salt);
            }

            // 使用EVP_BytesToKey派生密钥和IV
            var (key, iv) = EvpBytesToKey(passphrase + Encoding.UTF8.GetString(salt), aes.KeySize, blockSize);
            aes.Key = key;
            aes.IV = iv;

            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using (var msEncrypt = new MemoryStream())
            {
                // 写入CryptoJS的格式前缀:Salted__ + salt
                msEncrypt.Write(Encoding.ASCII.GetBytes("Salted__"), 0, 8);
                msEncrypt.Write(salt, 0, salt.Length);

                using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    // 指定UTF-8编码写入明文
                    using (var swEncrypt = new StreamWriter(csEncrypt, Encoding.UTF8))
                    {
                        swEncrypt.Write(plainText);
                    }
                }

                return Convert.ToBase64String(msEncrypt.ToArray());
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: " + ex.Message);
        return null;
    }
}

补充说明

  • 若想让CryptoJS使用PBKDF2而非默认的EVP_BytesToKey,需要在Node.js代码中手动指定密钥派生参数,但两边必须统一配置,不如直接匹配默认行为更简单。
  • 修正后的代码生成的加密字符串,完全可以被你提供的Node.js解密代码正常解密。

内容的提问来源于stack exchange,提问作者maryam mohammadbagheri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 20:52:05