Сбой шифрования AES в последней строке файла при использовании того же FileStream, что и CryptoStream
Я пытаюсь создать метод шифрования 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 ответ
Это было то, что я пытался описать в комментариях, и это работает.
Я не удосужился сделать 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);
}
}
}