You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C# CryptoStream读写同一文件时的大小异常及解密失败问题

AES文件加密/解密问题修复

核心问题分析

  1. 加密后文件大小变化:
    你使用的PaddingMode.PKCS7会自动对不足块大小的数据进行填充,当原文件大小刚好是块大小(16字节,对应代码中BlockSize=128位)的整数倍时,会额外填充一个完整块(16字节),因此128字节的文件加密后变为144字节是PKCS7填充的正常行为。调试时提前终止未触发CryptoStream的FlushFinalBlock,导致填充未写入,所以大小看似正常,但这种加密是不完整的,无法正确解密。

  2. 解密失败:

    • 代码中先创建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);
        }
    }
}

关键修改说明

  1. 参数设置顺序:先配置好AES的所有参数(密钥、IV、模式、填充等),再创建加密/解密器,确保加密器使用正确的参数。
  2. 分离读写流:使用临时文件存储加密/解密结果,避免在同一个文件流上同时读写导致的位置指针混乱,同时防止操作失败时原文件损坏。
  3. 缓冲区优化:将缓冲区从128字节改为1024字节,提升IO效率。
  4. 正确处理填充:CryptoStream的using块会自动调用FlushFinalBlock,确保PKCS7填充正确写入(加密)或移除(解密)。

注意:使用ASCIIEncoding将密码转换为密钥/IV存在风险——如果密码包含非ASCII字符会丢失信息,建议使用Rfc2898DeriveBytes从密码生成密钥和IV,避免直接使用密码作为密钥/IV。

内容的提问来源于stack exchange,提问作者Eloy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:06:07