React Native加密数据在.NET 7解密报错:填充无效问题求助
解决React Native与.NET 7 AES加解密的「Padding is invalid and cannot be removed.」错误
问题根源
出现该错误的核心是前后端加解密参数完全不匹配,具体问题如下:
- IV长度违规:AES-CBC要求IV必须为16字节(128位),前端生成了17字节的IV,直接导致加密数据格式异常
- IV使用逻辑错误:后端未使用前端加密时生成的随机IV,反而用密钥派生函数生成IV,完全不符合AES-CBC规范
- 密钥不一致:前端直接使用原始密钥,后端通过PBKDF2派生密钥,两者密钥完全不匹配
- 数据格式处理错误:前端未将IV转为Base64字符串传输,后端无法解析;同时加密结果重复调用
toString()属于冗余操作
修正后的React Native加密代码
import CryptoJS from 'crypto-js'; import { key } from '../../secretKey'; // 确保此处key与后端密钥派生规则一致 export function Encrypt(data) { // 生成符合AES-CBC要求的16字节随机IV const iv = CryptoJS.lib.WordArray.random(16); // 加密数据,若后端用PBKDF2派生密钥,前端需执行相同派生逻辑 const encryptedData = CryptoJS.AES.encrypt(data, key, { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: iv, }).toString(); return { cipherText: encryptedData, // 直接使用加密后的字符串,无需重复toString() iv: iv.toString(CryptoJS.enc.Base64) // 将IV转为Base64字符串,方便后端解析 }; }
修正后的.NET 7解密代码
DecryptionHandler.cs
using System; using System.IO; using System.Security.Cryptography; using System.Text; public static class EncryptionHandler { // 前后端必须使用完全一致的密钥派生参数 private static byte[] DeriveKeyFromPassword(string password) { var salt = Encoding.UTF8.GetBytes("your-shared-secure-salt"); // 禁止使用空盐,盐需前后端一致 var iterations = 10000; // 提高迭代次数增强安全性 var desiredKeyLength = 16; var hashMethod = HashAlgorithmName.SHA384; return Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), // 统一使用UTF-8编码,避免差异 salt, iterations, hashMethod, desiredKeyLength); } // 修改方法,接收前端传来的密文和IV public static string DecryptStringAES(string cipherText, string ivBase64) { var keyBytes = DeriveKeyFromPassword(keySecret); // 派生密钥,与前端逻辑一致 var ivBytes = Convert.FromBase64String(ivBase64); // 解析前端传来的Base64格式IV var encryptedBytes = Convert.FromBase64String(cipherText); return DecryptStringFromBytes(encryptedBytes, keyBytes, ivBytes); } private static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv) { if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException(nameof(cipherText)); if (key == null || key.Length <= 0) throw new ArgumentNullException(nameof(key)); if (iv == null || iv.Length <= 0) throw new ArgumentNullException(nameof(iv)); string plaintext = null; // 使用官方推荐的Aes类替代已过时的RijndaelManaged using (var aes = Aes.Create()) { aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.KeySize = 128; aes.BlockSize = 128; aes.Key = key; aes.IV = iv; var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); try { using (var msDecrypt = new MemoryStream(cipherText)) using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) using (var srDecrypt = new StreamReader(csDecrypt)) { plaintext = srDecrypt.ReadToEnd(); } } catch (Exception ex) { plaintext = ex.Message; } } return plaintext; } }
API控制器代码修改
// 确保模型包含正确的字段名(修正拼写:cipher而非chiper) public class RegisterModel { public string CipherText { get; set; } public string Iv { get; set; } } [HttpPost("decrypt")] public IActionResult Decrypt([FromBody] RegisterModel model) { string decryptedData = EncryptionHandler.DecryptStringAES(model.CipherText, model.Iv); return Ok(new { decryptedData }); }
核心安全规范
- IV必须随机唯一:每次加密生成新的随机IV,绝不能重复使用
- 密钥派生完全一致:前后端的派生算法、盐、迭代次数、编码格式必须完全匹配
- 禁用空盐:PBKDF2空盐会大幅降低安全性,必须使用保密的共享盐
- 统一编码格式:字符串转字节时统一使用UTF-8,避免编码差异导致密钥不匹配
- 使用官方推荐API:.NET中优先使用
Aes.Create()替代已过时的RijndaelManaged
内容的提问来源于stack exchange,提问作者Mahdi Guliyev
相关产品推荐
相关产品推荐

