C# AES加密字节数组后,Angular14解密转Base64显示PDF异常求助
问题分析与解决方案
核心问题点
- 客户端字节数组转WordArray方式错误:直接将API返回的单字节数组传入WordArray构造函数,会导致字节被错误解析为32位整数,破坏加密数据结构。
- 解密函数用法不符合CryptoJS规范:错误构造CipherParams并传入解密方法,导致解密逻辑异常。
- Base64转换逻辑错误:直接处理WordArray的
words属性(32位整数数组)会导致字节顺序混乱,生成损坏的Base64字符串。 - 服务端潜在密钥长度问题: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
相关产品推荐
相关产品推荐

