.NET 7文件加密正确实现方案?RijndaelManaged已过时
.NET 7 AES文件加解密问题修复
问题背景
在.NET 7解决方案中实现自定义文件加解密时,采用Aes.Create()替代已过时的RijndaelManaged,但出现以下问题:
- 加解密过程偶尔抛出异常
- 所有类型文件(文本、图片、二进制文件)解密后均损坏,无法恢复原始数据
代码错误分析
你的代码存在以下关键问题:
- Aes实例被提前释放:
GetAES方法中用using包裹Aes.Create(),导致返回的Aes对象已被Dispose,后续调用加密/解密方法时会出现异常或行为异常。 - Salt读取不完整:解密时仅调用一次
Read读取salt,无法保证一次性读取全部32字节,会导致密钥/IV推导错误。 - 未处理加密最终块:加密时未调用
CryptoStream.FlushFinalBlock(),最后一块带填充的加密数据未写入输出流,造成文件截断损坏。 - 冗余的手动Close调用:所有Stream已通过
using自动管理生命周期,手动调用Close()属于多余操作,可能引发资源冲突。 - 空catch吞掉异常:异常被无处理捕获,无法定位问题根源,阻碍调试。
- 填充模式选择不当:
PaddingMode.Zeros会破坏末尾含0的二进制文件,标准PaddingMode.PKCS7更安全可靠。
修正后的完整代码
共用Aes初始化方法
private static Aes GetAES(string password, byte[] salt) { byte[] passwordBytes = Encoding.UTF8.GetBytes(password); Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000, HashAlgorithmName.SHA256); Aes aes = Aes.Create(); aes.KeySize = 256; aes.BlockSize = 128; aes.Padding = PaddingMode.PKCS7; // 使用标准PKCS7填充 aes.Mode = CipherMode.CBC; aes.Key = key.GetBytes(aes.KeySize / 8); aes.IV = key.GetBytes(aes.BlockSize / 8); return aes; }
加密方法
public static void EncryptFile(string sourcePath, string destinationPath, string password) { if (string.IsNullOrWhiteSpace(password)) return; using FileStream outputStream = File.OpenWrite(destinationPath); byte[] salt = RandomNumberGenerator.GetBytes(32); outputStream.Write(salt, 0, salt.Length); using Aes aes = GetAES(password, salt); // 用using管理Aes生命周期 using CryptoStream cryptoStream = new CryptoStream(outputStream, aes.CreateEncryptor(), CryptoStreamMode.Write); using FileStream inputStream = File.OpenRead(sourcePath); byte[] buffer = new byte[1024 * 1024]; int readPosition; while ((readPosition = inputStream.Read(buffer, 0, buffer.Length)) > 0) { cryptoStream.Write(buffer, 0, readPosition); } cryptoStream.FlushFinalBlock(); // 必须调用,确保最后一块加密数据写入 }
解密方法
public static void DecryptFile(string sourcePath, string destinationPath, string password) { if (string.IsNullOrWhiteSpace(password)) return; using FileStream inputStream = File.OpenRead(sourcePath); byte[] salt = new byte[32]; int totalRead = 0; // 循环读取确保salt完全读取 while (totalRead < salt.Length) { int read = inputStream.Read(salt, totalRead, salt.Length - totalRead); if (read == 0) { throw new InvalidDataException("加密文件损坏,无法读取完整的salt"); } totalRead += read; } using Aes aes = GetAES(password, salt); using CryptoStream cryptoStream = new CryptoStream(inputStream, aes.CreateDecryptor(), CryptoStreamMode.Read); using FileStream outputStream = File.Create(destinationPath); byte[] buffer = new byte[1024 * 1024]; int readPosition; while ((readPosition = cryptoStream.Read(buffer, 0, buffer.Length)) > 0) { outputStream.Write(buffer, 0, readPosition); } }
关键修改说明
- 移除
GetAES中的using声明,改为由调用方用using管理Aes实例生命周期 - 解密时循环读取salt,确保获取完整的32字节数据
- 加密时添加
FlushFinalBlock()调用,保证最后一块加密数据(含填充)写入输出流 - 替换填充模式为
PaddingMode.PKCS7,适配所有类型文件 - 移除所有手动
Close()调用,依赖using自动释放资源 - 移除空catch块,让异常正常抛出以便调试
内容的提问来源于stack exchange,提问作者blcamp
相关产品推荐
相关产品推荐

