使用C# CryptoStream读写同一文件时的大小异常及解密失败问题
AES文件加密/解密问题修复
核心问题分析
加密后文件大小变化:
你使用的PaddingMode.PKCS7会自动对不足块大小的数据进行填充,当原文件大小刚好是块大小(16字节,对应代码中BlockSize=128位)的整数倍时,会额外填充一个完整块(16字节),因此128字节的文件加密后变为144字节是PKCS7填充的正常行为。调试时提前终止未触发CryptoStream的FlushFinalBlock,导致填充未写入,所以大小看似正常,但这种加密是不完整的,无法正确解密。解密失败:
- 代码中先创建
CreateEncryptor()/CreateDecryptor(),再设置AES的密钥、IV、模式、填充等参数,这是完全错误的——加密/解密器创建时已基于当时的默认参数生成,后续修改参数不会生效。 - 直接在同一个
FileStream上同时读写,位置指针处理逻辑混乱,导致读取加密数据和写入解密数据时互相干扰,最终解密失败。
- 代码中先创建
修正后的代码
加密函数
public static void EncryptFileB(string strInputFilename, string strPassword) { // 先准备AES参数 using (RijndaelManaged aesCrypto = new RijndaelManaged()) { aesCrypto.KeySize = 256; aesCrypto.BlockSize = 128; aesCrypto.Key = ASCIIEncoding.ASCII.GetBytes(strPassword); aesCrypto.IV = ASCIIEncoding.ASCII.GetBytes(strPassword); aesCrypto.Padding = PaddingMode.PKCS7; aesCrypto.Mode = CipherMode.CBC; // 使用临时文件存储加密结果,避免原文件读写冲突 string tempFilePath = Path.GetTempFileName(); try { using (FileStream fsIn = new FileStream(strInputFilename, FileMode.Open, FileAccess.Read)) using (FileStream fsOut = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write)) using (CryptoStream cs = new CryptoStream(fsOut, aesCrypto.CreateEncryptor(), CryptoStreamMode.Write)) { byte[] buffer = new byte[1024]; // 使用更大的缓冲区提升效率 int bytesRead; while ((bytesRead = fsIn.Read(buffer, 0, buffer.Length)) > 0) { cs.Write(buffer, 0, bytesRead); } } // 替换原文件 File.Delete(strInputFilename); File.Move(tempFilePath, strInputFilename); } finally { if (File.Exists(tempFilePath)) File.Delete(tempFilePath); } } }
解密函数
public static void DecryptFileB(string strInputFilename, string strPassword) { using (RijndaelManaged aesCrypto = new RijndaelManaged()) { aesCrypto.KeySize = 256; aesCrypto.BlockSize = 128; aesCrypto.Key = ASCIIEncoding.ASCII.GetBytes(strPassword); aesCrypto.IV = ASCIIEncoding.ASCII.GetBytes(strPassword); aesCrypto.Padding = PaddingMode.PKCS7; aesCrypto.Mode = CipherMode.CBC; string tempFilePath = Path.GetTempFileName(); try { using (FileStream fsIn = new FileStream(strInputFilename, FileMode.Open, FileAccess.Read)) using (FileStream fsOut = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write)) using (CryptoStream cs = new CryptoStream(fsIn, aesCrypto.CreateDecryptor(), CryptoStreamMode.Read)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = cs.Read(buffer, 0, buffer.Length)) > 0) { fsOut.Write(buffer, 0, bytesRead); } } File.Delete(strInputFilename); File.Move(tempFilePath, strInputFilename); } finally { if (File.Exists(tempFilePath)) File.Delete(tempFilePath); } } }
关键修改说明
- 参数设置顺序:先配置好AES的所有参数(密钥、IV、模式、填充等),再创建加密/解密器,确保加密器使用正确的参数。
- 分离读写流:使用临时文件存储加密/解密结果,避免在同一个文件流上同时读写导致的位置指针混乱,同时防止操作失败时原文件损坏。
- 缓冲区优化:将缓冲区从128字节改为1024字节,提升IO效率。
- 正确处理填充:
CryptoStream的using块会自动调用FlushFinalBlock,确保PKCS7填充正确写入(加密)或移除(解密)。
注意:使用
ASCIIEncoding将密码转换为密钥/IV存在风险——如果密码包含非ASCII字符会丢失信息,建议使用Rfc2898DeriveBytes从密码生成密钥和IV,避免直接使用密码作为密钥/IV。
内容的提问来源于stack exchange,提问作者Eloy
相关产品推荐
相关产品推荐

