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

如何用Crypto-JS解密C#后端AES加密内容?解密空串问题排查

AES解密结果为空的问题排查与修复

问题描述

前端Angular使用Crypto-JS解密后端C# AES加密值时,解密结果始终为空字符串,相关代码如下:

C# 加密代码

// 调用示例:EncryptAES("XEMFkT92UtR1VJI8kU8XQJALk98GGEFM", "random text to encrypt");
public static string EncryptAES(string passPhrase, string plainText)
{
    byte[] iv = Generate256BitsOfRandomEntropy();
    byte[] temp;
    byte[] array;

    using (Aes aes = Aes.Create())
    {
        byte[] salt = Generate256BitsOfRandomEntropy();

        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(passPhrase, salt, 100);

        aes.Key = pdb.GetBytes(32);
        aes.KeySize = 256;
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;
        aes.IV = iv;

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

        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream, Encoding.UTF8))
                {
                    streamWriter.Write(plainText);
                }

                temp = memoryStream.ToArray();
                array = salt.Concat(iv).Concat(temp).ToArray();
                
                cryptoStream.Flush();
                encryptor.Dispose();
            }
        }
    }

    return Convert.ToBase64String(array);
}

// 随机字节数组生成器
private static byte[] Generate256BitsOfRandomEntropy()
{
    var randomBytes = new byte[16];
    using (var rngCsp = new RNGCryptoServiceProvider())
    {
        rngCsp.GetBytes(randomBytes);
    }
    return randomBytes;
}

前端Angular解密代码(原错误版本)

// 参数key与C#一致:XEMFkT92UtR1VJI8kU8XQJALk98GGEFM
// 参数toDecrypt是后端返回的Base64字符串
decryptAES(key: string, toDecrypt: string) {
    var data = Buffer.from(toDecrypt, 'base64');
    var salt = data.slice(0, 16); // 前16字节是salt
    var iv = data.slice(16, 32); // 接下来16字节是IV

    const wordArrayIV = CryptoJS.lib.WordArray.create(Array.from(iv));
    const wordArraySalt = CryptoJS.lib.WordArray.create(Array.from(salt))

    var keyPBKDF2 = CryptoJS.PBKDF2(key, wordArraySalt, {
        keySize: 256 / 32,
        iterations: 100
    });

    var decrypted = CryptoJS.AES.decrypt(toDecrypt, keyPBKDF2, 
        {   
            mode: CryptoJS.mode.CBC, 
            padding: CryptoJS.pad.Pkcs7, 
            iv: wordArrayIV
        });

    // 返回空字符串
    return decrypted.toString();
}

问题原因

  1. 解密传入数据错误:CryptoJS.AES.decrypt需要传入纯密文部分,但原代码传入了包含salt+iv+密文的完整Base64串,导致无法正确解析密文。
  2. 字节转WordArray方式错误:用Array.from(iv)转换字节数组会破坏原始字节结构,CryptoJS无法正确识别IV和salt。
  3. 字符串转换方式错误:decrypted.toString()默认返回十六进制字符串,需指定UTF-8编码才能得到明文。

修复后的前端解密代码

decryptAES(key: string, toDecrypt: string) {
    // 用CryptoJS直接解析Base64,避免Buffer转换问题
    const fullData = CryptoJS.enc.Base64.parse(toDecrypt);
    
    // 拆分salt(前16字节)、IV(中间16字节)、密文(剩余部分)
    const salt = CryptoJS.lib.WordArray.create(fullData.words.slice(0, 4));
    const iv = CryptoJS.lib.WordArray.create(fullData.words.slice(4, 8));
    const ciphertext = CryptoJS.lib.WordArray.create(fullData.words.slice(8));

    // 生成密钥,与C#的Rfc2898DeriveBytes对齐
    const keyPBKDF2 = CryptoJS.PBKDF2(key, salt, {
        keySize: 256 / 32,
        iterations: 100,
        hasher: CryptoJS.algo.SHA1 // Rfc2898DeriveBytes默认用SHA1,显式指定更清晰
    });

    // 解密时传入纯密文对象
    const decrypted = CryptoJS.AES.decrypt(
        { ciphertext: ciphertext },
        keyPBKDF2,
        {
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7,
            iv: iv
        }
    );

    // 转换为UTF-8明文
    return decrypted.toString(CryptoJS.enc.Utf8);
}

额外说明

  • C#的Rfc2898DeriveBytes默认使用SHA1哈希算法,CryptoJS的PBKDF2默认也是SHA1,显式指定可避免版本差异问题。
  • 加密时C#将salt+iv+密文拼接后转Base64,解密时必须严格按顺序拆分这三部分,不能混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:01:07