当使用与 CryptoStream 相同的 FileStream 时,AES 加密在文件的最后一行失败
AES Encryption failed on last line of file when using same FileStream as CryptoStream
我正在尝试创建一种同时读取和写入同一文件的 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
这就是我试图在评论中描述的那种东西,它起作用了。
我没有费心在密钥上执行 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);
}
}
}
我正在尝试创建一种同时读取和写入同一文件的 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
这就是我试图在评论中描述的那种东西,它起作用了。
我没有费心在密钥上执行 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);
}
}
}