AES解密报错:The input data is not a complete block问题求助
AES加解密错误排查与修复
核心问题分析
以下是导致你遇到System.Security.Cryptography.CryptographicException错误的核心问题:
- 加密结果直接转UTF8字符串导致数据损坏:加密后的字节数组是二进制数据,并非合法的UTF8编码序列,直接用
Encoding.UTF8.GetString(crypted)转换会导致部分字节丢失或被篡改,解密时输入的字节数组长度或内容不符合AES要求,触发"输入数据不是完整块"的错误。 - 解密时重新生成IV:CBC模式下,AES的加密和解密必须使用同一个初始化向量(IV),你当前解密代码中调用
aes.GenerateIV()生成新的IV,与加密时的IV不匹配,这不仅会导致解密失败,也会引发数据块相关的异常。
修复方案
- 用Base64编码传输加密结果:将加密后的字节数组转为Base64字符串,而非直接转UTF8,Base64是专门用于二进制数据的文本编码,能保证数据完整无损。
- 传递并复用加密时的IV:加密时需要把生成的IV和密文一起保存/传输,解密时使用同一个IV进行解密。通常的做法是将IV前缀到密文字节数组前,或者与密文分开存储。
修正后的完整代码
加密方法
private static string CreateRequestVerificationToken() { var key = System.Configuration.ConfigurationManager.AppSettings["EncryptionKey"]; // 验证密钥长度:128位AES要求密钥是16字节(UTF8下的16个字符) if (Encoding.UTF8.GetBytes(key).Length != 16) { throw new ArgumentException("EncryptionKey must be 16 UTF8 characters for 128-bit AES"); } var unprotectedData = string.Format("{0}|{1}", "A", "B"); var src = Encoding.UTF8.GetBytes(unprotectedData); using (var aes = Aes.Create()) { aes.BlockSize = 128; aes.KeySize = 128; aes.Key = Encoding.UTF8.GetBytes(key); aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.Zeros; aes.GenerateIV(); // 生成随机IV using (ICryptoTransform encrypt = aes.CreateEncryptor(aes.Key, aes.IV)) { var crypted = encrypt.TransformFinalBlock(src, 0, src.Length); // 将IV和密文合并:IV在前(16字节),密文在后 var combinedBytes = new byte[aes.IV.Length + crypted.Length]; Buffer.BlockCopy(aes.IV, 0, combinedBytes, 0, aes.IV.Length); Buffer.BlockCopy(crypted, 0, combinedBytes, aes.IV.Length, crypted.Length); // 转为Base64字符串返回 return Convert.ToBase64String(combinedBytes); } } }
解密方法
private static string DecryptData(string encryptedToken) { var key = System.Configuration.ConfigurationManager.AppSettings["EncryptionKey"]; if (Encoding.UTF8.GetBytes(key).Length != 16) { throw new ArgumentException("EncryptionKey must be 16 UTF8 characters for 128-bit AES"); } // 将Base64字符串转回字节数组 var combinedBytes = Convert.FromBase64String(encryptedToken); var ivLength = 16; // 128位AES的IV固定是16字节 // 分离IV和密文 var iv = new byte[ivLength]; var protectedData = new byte[combinedBytes.Length - ivLength]; Buffer.BlockCopy(combinedBytes, 0, iv, 0, ivLength); Buffer.BlockCopy(combinedBytes, ivLength, protectedData, 0, protectedData.Length); using var aes = Aes.Create(); aes.BlockSize = 128; aes.KeySize = 128; aes.Key = Encoding.UTF8.GetBytes(key); aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.Zeros; aes.IV = iv; // 使用加密时的IV,不再生成新的 using ICryptoTransform decrypt = aes.CreateDecryptor(aes.Key, aes.IV); byte[] decryptedText = decrypt.TransformFinalBlock(protectedData, 0, protectedData.Length); // 去掉PaddingMode.Zeros添加的末尾零字节,转为字符串 return Encoding.UTF8.GetString(decryptedText).TrimEnd('\0'); }
额外注意事项
- 密钥验证:128位AES要求密钥长度为16字节(UTF8编码下的16个字符),如果你的密钥长度不符,需要调整密钥或者改用256位AES(密钥32字节)。
- Padding选择:
PaddingMode.Zeros可能会导致原始数据末尾有零的情况丢失,建议优先使用PaddingMode.PKCS7(AES的标准填充方式),这样解密时能自动移除填充字节,无需手动处理。 - Aes实现选择:
Aes.Create()会返回当前平台最优的AES实现,推荐优先使用,而AesCryptoServiceProvider是Windows特定的实现。
内容的提问来源于stack exchange,提问作者Wasyster
相关产品推荐
相关产品推荐

