Сбой шифрования AES в последней строке файла при использовании того же FileStream, что и CryptoStream - PullRequest
0 голосов
/ 17 февраля 2019

Я пытаюсь создать метод шифрования AES, в котором один и тот же файл считывается и записывается одновременно.Другие коды, которые я прочитал, создают новый файл, я не хочу иметь новый файл.Другая зашифрованная строка соответствовала моему справочному выводу из отдельного вывода, за исключением того, что мой метод генерировал неверную последнюю строку в виде обычного текста плюс дополнительные байты.

Я попытался заменить аргумент FileStream CryptoStream и удалить fs.Seek () в строке 58, программа работает правильно и сгенерировал новый зашифрованный файл .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();
}

Используемый мной ввод: Длинный английский файл

1 Ответ

0 голосов
/ 18 февраля 2019

Это было то, что я пытался описать в комментариях, и это работает.

Я не удосужился сделать 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);
        }
    }
}
...