.NET 6 AES极简实现解密报错:输入数据不是完整块
我查了很多相关讨论都没解决问题,先贴我原本觉得可行的极简实现代码:
public class CustomEncryptor : IEncryptor { public string Key { get; } // 32字节,比如 6GUNtgba8Rqgm5tc9JGhzxjN6XbzuyNM public CustomEncryptor(string key) { Key = key; } public byte[] Encrypt(byte[] data) { using var aes = Aes.Create(); aes.Key = Encoding.UTF8.GetBytes(Key); aes.IV = RandomNumberGenerator.GetBytes(16); aes.Padding = PaddingMode.PKCS7; return aes.EncryptCbc(data, aes.IV); } public byte[] Decrypt(byte[] data) { using var aes = Aes.Create(); aes.Key = Encoding.UTF8.GetBytes(Key); aes.IV = RandomNumberGenerator.GetBytes(16); aes.Padding = PaddingMode.PKCS7; return aes.DecryptCbc(data, aes.IV); } }
加密数据是正常的,但解密时一直抛出这个异常:
System.Security.Cryptography.CryptographicException: The input data is not a complete block.
我用了AES内置方法,提供32字节Key,生成16字节IV,按理说应该没问题。试过用new byte[16]空数组当IV,换不同填充模式,都没用。
补充背景:我用扩展方法简化字符串加密解密场景,实际使用是用户操作后加密数据存储,应用重启后解密使用:
public static string Encrypt(this IEncryptor encryptor, string textToEncrypt) { var textAsBytes = Encoding.UTF8.GetBytes(textToEncrypt); var encryptedData = encryptor.Encrypt(textAsBytes); return Convert.ToBase64String(encryptedData); } public static string Decrypt(this IEncryptor encryptor, string encryptedText) { var textAsBytes = Convert.FromBase64String(encryptedText); var decryptedData = encryptor.Decrypt(textAsBytes); return Encoding.UTF8.GetString(decryptedData); }
网上很多示例都是类似写法,我实在搞不懂哪里错了。
更新1
看到评论说加密解密的Key和IV必须一致,我改成了显式设置相同Key和IV的版本,代码如下:
public class CustomEncryptor : IEncryptor { public byte[] Key { get; } public byte[] IV { get; } public CustomEncryptor(byte[] key, byte[] iv) { IV = iv; Key = key; } public byte[] Encrypt(byte[] data) { using var aes = Aes.Create(); aes.Key = Key; return aes.EncryptCbc(data, IV); } public byte[] Decrypt(byte[] data) { using var aes = Aes.Create(); aes.Key = Key; return aes.DecryptCbc(data, IV); } } // 使用示例 var key = Encoding.UTF8.GetBytes("UxRBN8hfjzTG86d6SkSSNzyUhERGu5Zj"); // 32字节 var iv = Encoding.UTF8.GetBytes("7cA8jkRMJGZ8iMeJ"); // 16字节 services.AddSingleton<IEncryptor>(new CustomEncryptor(key, iv));
但还是报同样的错误。
问题根源与解决办法
1. 初始版本的致命错误:IV未随密文存储
AES-CBC模式要求解密必须使用加密时的同一个IV。你最初的代码里,加密时生成随机IV,但只返回了加密后的密文,解密时又重新生成新的随机IV,这完全不符合CBC模式的要求,必然解密失败。而且EncryptCbc方法仅返回密文,不包含IV,必须手动把IV和密文拼接后存储,解密时再拆分使用。
2. 更新1的问题:IV生成不规范+潜在的密文完整性问题
你用UTF8字符串转字节生成IV,首先固定IV严重违反加密安全规范(相同明文会生成相同密文),其次如果字符串转字节后长度不符合AES要求(比如含多字节字符),会导致IV异常。另外你需要确认加密后的密文是否完整传递到解密环节——比如存储时字段长度不足截断了密文,也会触发「输入数据不是完整块」的错误。
正确实现方式(安全且符合规范)
推荐使用随机IV,将IV与密文拼接存储,解密时拆分:
public class CustomEncryptor : IEncryptor { private readonly byte[] _key; public CustomEncryptor(byte[] key) { if (key.Length != 32) // AES-256要求32字节密钥 throw new ArgumentException("密钥必须为32字节", nameof(key)); _key = key; } public byte[] Encrypt(byte[] data) { using var aes = Aes.Create(); aes.Key = _key; aes.GenerateIV(); // 生成随机安全的IV aes.Padding = PaddingMode.PKCS7; var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using var ms = new MemoryStream(); // 先写入IV到密文头部(前16字节) ms.Write(aes.IV, 0, aes.IV.Length); using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write); cs.Write(data, 0, data.Length); cs.FlushFinalBlock(); return ms.ToArray(); } public byte[] Decrypt(byte[] data) { if (data.Length < 16) throw new ArgumentException("密文格式错误", nameof(data)); using var aes = Aes.Create(); aes.Key = _key; aes.Padding = PaddingMode.PKCS7; // 从密文头部读取IV(前16字节) var iv = new byte[16]; Array.Copy(data, 0, iv, 0, iv.Length); aes.IV = iv; var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); // 跳过IV部分,读取实际密文 using var ms = new MemoryStream(data, iv.Length, data.Length - iv.Length); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); using var outputMs = new MemoryStream(); cs.CopyTo(outputMs); return outputMs.ToArray(); } } // 原扩展方法无需修改,可直接使用
额外注意事项
- 永远不要用固定IV,这会彻底破坏AES的安全性,必须使用随机IV。
- 密钥不要直接用UTF8字符串转换,推荐使用密码派生函数(如
Rfc2898DeriveBytes)从用户密码生成,或者直接使用随机生成的字节数组作为密钥。 - 检查密文存储环节是否存在截断问题(比如数据库字段长度不足),这也是触发该异常的常见原因。
内容的提问来源于stack exchange,提问作者Grofit

