使用与CryptoStream相同的FileStream时,文件最后一行的AES加密失败

时间:2019-02-17 13:35:41

标签: c# aes rijndaelmanaged

我正在尝试创建一种AES加密方法,其中同时读取和写入同一文件。我阅读的所有其他代码都创建了一个新文件,我不想拥有新文件。其他加密的行与我的参考输出分别来自单独的输出,除了我的方法生成的不正确的最后一行是纯文本加上其他字节。

我尝试替换CryptoStream的FileStream参数并删除第58行的fs.Seek(),程序正常运行并生成了新的.aes加密文件,供上面的参考输出使用。

static void Main(string[] args)
{
    AESCryptFile(@"C:\Users\user\Downloads\test.txt",
        new byte[] { 0x13, 0x11, 0x7F, 0x08, 0x45, 0x2E, 0x96, 0x33 },
        new byte[] { 0x13, 0x11, 0x7F, 0x08, 0x45, 0x2E, 0x96, 0x33 }, false);
    Console.WriteLine("Done");
    Console.ReadLine();
}

public async static void AESCryptFile
    (string path, byte[] key, byte[] iv, bool encrypt)
{
    // validation
    if (path == null || !File.Exists(path))
        throw new FileNotFoundException("Path does not exist");
    if (key == null || key.Length == 0)
        throw new ArgumentNullException("Password is null");
    if (iv == null || iv.Length < 8)
        throw new ArgumentException("IV is null or under 8 bytes long");

    // in and out stream for files
    FileStream fs = new FileStream
        (path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);

    // initialize aes with safe hash and mode
    RijndaelManaged algo = new RijndaelManaged();
    Rfc2898DeriveBytes hashed = new Rfc2898DeriveBytes(key, iv, 25000);
    algo.Key = hashed.GetBytes(32);
    algo.IV = hashed.GetBytes(16);
    algo.Padding = PaddingMode.PKCS7;
    algo.Mode = CipherMode.CFB;

    // mediator stream for cryptography
    CryptoStream cs = new CryptoStream(fs,
        encrypt ? algo.CreateEncryptor() : algo.CreateDecryptor(),
        encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read);

    // main file transfer and crypto
    await Task.Run(
        () => readCryptWrite(new FileInfo(path).Length, fs, cs, encrypt));
}

private static void readCryptWrite(long fileSize, FileStream fs,
    CryptoStream cs, bool encrypt)
{
    // 1 MB of buffer zone allocation
    byte[] buffer = new byte[1048576];
    int nextBlockSize;
    long processedSize = 0;
    // loop while there are more to read
    while ((nextBlockSize = encrypt ? fs.Read(buffer, 0, buffer.Length) :
        cs.Read(buffer, 0, buffer.Length)) > 0)
    {
        // set pointer back to last written space
        fs.Seek(processedSize, SeekOrigin.Begin);
        // write out to file
        if (encrypt) cs.Write(buffer, 0, nextBlockSize);
        else fs.Write(buffer, 0, nextBlockSize);
        // set progress
        processedSize = fs.Position;
        SetProgress?.Invoke((int)((double)processedSize / fileSize * 100));
        if (nextBlockSize != buffer.Length) break;
    }
    // close streams
    cs.Clear();
    cs.Close();
    fs.Close();
}

我使用的输入:Long English file

1 个答案:

答案 0 :(得分:1)

这是我试图在评论中描述的东西,并且有效。

我没有费心在密钥上执行Rfc2898DeriveBytes,检查文件是否存在,等等。但是,它确实随机生成了一个IV,并将其写入文件的末尾(结果比写入它容易得多)一开始)。

private static void EncryptFile(string path, byte[] key)
{
    byte[] iv = new byte[16];
    // private static readonly RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider();
    rngcsp.GetBytes(iv);

    using (var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
    {
        byte[] buffer = new byte[1024];
        long readPos = 0;
        long writePos = 0;
        long readLength = fs.Length;

        using (var aes = new RijndaelManaged()
        {
            Key = key,
            IV = iv,
            Padding = PaddingMode.PKCS7,
            Mode = CipherMode.CFB,
        })
        using (var cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            while (readPos < readLength)
            {
                fs.Position = readPos;
                int bytesRead = fs.Read(buffer, 0, buffer.Length);
                readPos = fs.Position;

                fs.Position = writePos;
                cs.Write(buffer, 0, bytesRead);
                writePos = fs.Position;
            }

            // In older versions of .NET, CryptoStream doesn't have a ctor
            // with 'leaveOpen', so we have to do this instead.
            cs.FlushFinalBlock();

            // Write the IV to the end of the file
            fs.Write(iv, 0, iv.Length);
        }
    }
}

并解密:

private static void DecryptFile(string path, byte[] key)
{
    using (var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
    {
        byte[] buffer = new byte[1024];
        byte[] iv = new byte[16];
        long readPos = 0;
        long writePos = 0;
        long readLength = fs.Length - iv.Length;

        // IV is the last 16 bytes
        if (fs.Length < iv.Length)
            throw new IOException("File is too short");
        fs.Position = readLength;
        fs.Read(iv, 0, iv.Length);
        fs.SetLength(readLength);

        using (var aes = new RijndaelManaged()
        {
            Key = key,
            IV = iv,
            Padding = PaddingMode.PKCS7,
            Mode = CipherMode.CFB,
        })
        using (var cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read))
        {
            while (readPos < readLength)
            {
                fs.Position = readPos;
                int bytesRead = cs.Read(buffer, 0, buffer.Length);
                readPos = fs.Position;

                fs.Position = writePos;
                fs.Write(buffer, 0, bytesRead);
                writePos = fs.Position;
            }

            // Trim the padding
            fs.SetLength(writePos);
        }
    }
}