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

C# AES加密字节数组后,Angular14解密转Base64显示PDF异常求助

问题分析与解决方案

核心问题点

  1. 客户端字节数组转WordArray方式错误:直接将API返回的单字节数组传入WordArray构造函数,会导致字节被错误解析为32位整数,破坏加密数据结构。
  2. 解密函数用法不符合CryptoJS规范:错误构造CipherParams并传入解密方法,导致解密逻辑异常。
  3. Base64转换逻辑错误:直接处理WordArray的words属性(32位整数数组)会导致字节顺序混乱,生成损坏的Base64字符串。
  4. 服务端潜在密钥长度问题:AES密钥必须是16/24/32字节(对应AES-128/192/256),直接使用任意长度的字符串转字节可能引发异常或安全问题。

修正后的代码

服务端C#代码(优化版)

添加FlushFinalBlock()确保加密数据完整写入,同时增加密钥长度校验:

public byte[] EncryptAESData(byte[] bytesToEncrypt, string key)
{
    // 确保密钥长度符合AES要求:16(AES-128)/24(AES-192)/32(AES-256)字节
    byte[] keyBytes = Encoding.UTF8.GetBytes(key);
    if (keyBytes.Length != 16 && keyBytes.Length != 24 && keyBytes.Length != 32)
        throw new ArgumentException("密钥长度必须为16、24或32字节");

    byte[] iv = new byte[16]; // 注意:生产环境建议使用随机IV并随加密数据一起传输,不要固定为全0
    
    using (Aes aes = Aes.Create())
    {
        aes.Key = keyBytes;
        aes.IV = iv;
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;

        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(bytesToEncrypt, 0, bytesToEncrypt.Length);
                cs.FlushFinalBlock(); // 确保所有加密数据写入内存流
            }
            return ms.ToArray();
        }
    }
}

客户端Angular代码(修正版)

1. 添加字节数组转WordArray工具函数

private byteArrayToWordArray(bytes: number[]): CryptoJS.lib.WordArray {
    const uint8Array = new Uint8Array(bytes);
    const words: number[] = [];
    for (let i = 0; i < uint8Array.length; i++) {
        // 将单字节按大端顺序组装为32位整数
        words[i >>> 2] |= uint8Array[i] << (24 - (i % 4) * 8);
    }
    // 指定实际字节长度,避免WordArray自动补全
    return new CryptoJS.lib.WordArray(words, uint8Array.length);
}

2. 修正解密函数

decryptData(key: string, bytesToDecrypt: number[]): CryptoJS.lib.WordArray {
    const ciphertext = this.byteArrayToWordArray(bytesToDecrypt);
    const keyBytes = CryptoJS.enc.Utf8.parse(key);
    const iv = CryptoJS.lib.WordArray.create([0x00000000, 0x00000000, 0x00000000, 0x00000000]); // 对应服务端的全0IV

    // CryptoJS.AES.decrypt的正确用法:传入密文WordArray,在options中指定key和iv
    return CryptoJS.AES.decrypt({ ciphertext }, keyBytes, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
}

3. 正确转换为Base64字符串

// 解密并生成可用的Base64
let decryptedDoc = this.decryptData(key, this.document.file);
let decryptedBase64 = CryptoJS.enc.Base64.stringify(decryptedDoc);

// 用于PDF阅读器的完整Data URL(示例)
const pdfDataUrl = `data:application/pdf;base64,${decryptedBase64}`;

额外建议

  • 不要使用固定IV:生产环境中,每次加密应生成随机IV(16字节),并将IV与加密数据一起返回给客户端(IV不需要加密),大幅提升安全性。
  • 使用密钥派生函数:如果密钥是用户输入的密码,建议使用PBKDF2、Argon2等KDF生成符合长度的AES密钥,避免直接使用字符串转字节。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:25:08