逐块解密AES文件
Decrypting AES file chunk by chunk
我有一个用 256 位 AES 加密的 PDF 文件。如果我将整个文件读入内存中的一个字节数组,我可以很容易地解密它并且它 works:
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var buffer = await FileIO.ReadBufferAsync(file);
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
pwBuffer = pwBuffer.ToArray().Take(16).ToArray().AsBuffer();
var symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
var cryptographicKey = symProvider.CreateSymmetricKey(pwBuffer);
var res = CryptographicEngine.Decrypt(cryptographicKey, buffer, null);
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename,CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBufferAsync(destFile, res);
res = null;
return destFile;
}
问题是该文件不适合某些设备的内存。所以我试图想出一个 不需要内存中的整个文件一次的方法 。
我试过逐块读取输入文件并解密
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
pwBuffer = pwBuffer.ToArray().Take(16).ToArray().AsBuffer();
var symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
var cryptographicKey = symProvider.CreateSymmetricKey(pwBuffer);
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename, CreationCollisionOption.ReplaceExisting);
using (var fileStream = await destFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (var outputStream = fileStream.GetOutputStreamAt(0))
{
using (var dataWriter = new DataWriter(outputStream))
{
using (var inStream = await file.OpenAsync(FileAccessMode.Read))
{
using (var inReader = new DataReader(inStream.GetInputStreamAt(0)))
{
ulong read = 0;
while (read < inStream.Size)
{
var res = await inReader.LoadAsync(2048);
var data = new byte[res];
inReader.ReadBytes(data);
read = read + res;
var decrypted = CryptographicEngine.Decrypt(cryptographicKey, data.AsBuffer(0, data.Length), null).ToArray();
dataWriter.WriteBytes(decrypted.ToArray());
}
}
}
await dataWriter.StoreAsync();
dataWriter.DetachStream();
}
}
}
return destFile;
}
这不起作用,它在解密块时总是失败。我认为 2048 块大小可能是问题所在,但 使用此块大小可在完整的 .NET 中工作
var bytes = Encoding.UTF8.GetBytes(key);
var aes256 = new AesCryptoServiceProvider
{
Mode = CipherMode.ECB,
Key = bytes.Take(16).ToArray(),
Padding = PaddingMode.PKCS7
};
aes256.GenerateIV();
var encryptor = aes256.CreateDecryptor();
using (var dest = File.Create(destFile))
{
using (CryptoStream csEncrypt = new CryptoStream(dest, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(aes256.IV, 0, aes256.IV.Length);
using (Stream source = File.OpenRead(file)
{
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
csEncrypt.Write(buffer, 0, bytesRead);
}
}
csEncrypt.FlushFinalBlock();
}
}
有什么想法可以使 WinRT 代码正常工作吗?
如评论中所述,WinRT 不支持解密文件。使用 Portable.BouncyCastle
Nuget 包,我设法编写了有效的代码代码
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename, CreationCollisionOption.ReplaceExisting);
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
using (var inputStream = await file.OpenStreamForReadAsync())
{
using (var outputStream = await destFile.OpenStreamForWriteAsync())
{
var engine = CipherUtilities.GetCipher("AES/ECB/PKCS7Padding");
engine.Init(false, new KeyParameter(pwBuffer.ToArray().Take(16).ToArray()));
var size = inputStream.Length;
var current = inputStream.Position;
var chunkSize = 1024 * 1024L;
var lastChunk = false;
await Task.Yield();
// Initialize DataReader and DataWriter for reliable reading and writing
// to a stream. Writing directly to a stream is unreliable.
using (var reader = new BinaryReader(inputStream))
using (var writer = new BinaryWriter(outputStream))
{
while (current < size)
{
if (size - current < chunkSize)
{
chunkSize = (uint)(size - current);
lastChunk = true;
}
var chunk = new byte[chunkSize];
reader.Read(chunk, 0, (int)chunkSize);
// The last chunk must call DoFinal() as it appends additional bytes
var processedBytes = lastChunk ? engine.DoFinal(chunk) : engine.ProcessBytes(chunk);
writer.Write(processedBytes);
current = inputStream.Position;
await Task.Yield();
}
await outputStream.FlushAsync();
}
}
}
return destFile;
}
我有一个用 256 位 AES 加密的 PDF 文件。如果我将整个文件读入内存中的一个字节数组,我可以很容易地解密它并且它 works:
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var buffer = await FileIO.ReadBufferAsync(file);
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
pwBuffer = pwBuffer.ToArray().Take(16).ToArray().AsBuffer();
var symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
var cryptographicKey = symProvider.CreateSymmetricKey(pwBuffer);
var res = CryptographicEngine.Decrypt(cryptographicKey, buffer, null);
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename,CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBufferAsync(destFile, res);
res = null;
return destFile;
}
问题是该文件不适合某些设备的内存。所以我试图想出一个 不需要内存中的整个文件一次的方法 。
我试过逐块读取输入文件并解密
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
pwBuffer = pwBuffer.ToArray().Take(16).ToArray().AsBuffer();
var symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
var cryptographicKey = symProvider.CreateSymmetricKey(pwBuffer);
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename, CreationCollisionOption.ReplaceExisting);
using (var fileStream = await destFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (var outputStream = fileStream.GetOutputStreamAt(0))
{
using (var dataWriter = new DataWriter(outputStream))
{
using (var inStream = await file.OpenAsync(FileAccessMode.Read))
{
using (var inReader = new DataReader(inStream.GetInputStreamAt(0)))
{
ulong read = 0;
while (read < inStream.Size)
{
var res = await inReader.LoadAsync(2048);
var data = new byte[res];
inReader.ReadBytes(data);
read = read + res;
var decrypted = CryptographicEngine.Decrypt(cryptographicKey, data.AsBuffer(0, data.Length), null).ToArray();
dataWriter.WriteBytes(decrypted.ToArray());
}
}
}
await dataWriter.StoreAsync();
dataWriter.DetachStream();
}
}
}
return destFile;
}
这不起作用,它在解密块时总是失败。我认为 2048 块大小可能是问题所在,但 使用此块大小可在完整的 .NET 中工作
var bytes = Encoding.UTF8.GetBytes(key);
var aes256 = new AesCryptoServiceProvider
{
Mode = CipherMode.ECB,
Key = bytes.Take(16).ToArray(),
Padding = PaddingMode.PKCS7
};
aes256.GenerateIV();
var encryptor = aes256.CreateDecryptor();
using (var dest = File.Create(destFile))
{
using (CryptoStream csEncrypt = new CryptoStream(dest, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(aes256.IV, 0, aes256.IV.Length);
using (Stream source = File.OpenRead(file)
{
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
csEncrypt.Write(buffer, 0, bytesRead);
}
}
csEncrypt.FlushFinalBlock();
}
}
有什么想法可以使 WinRT 代码正常工作吗?
如评论中所述,WinRT 不支持解密文件。使用 Portable.BouncyCastle
Nuget 包,我设法编写了有效的代码代码
public static async Task<StorageFile> DecryptFile(IStorageFile file, string key, string destFilename)
{
var destFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(destFilename, CreationCollisionOption.ReplaceExisting);
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
using (var inputStream = await file.OpenStreamForReadAsync())
{
using (var outputStream = await destFile.OpenStreamForWriteAsync())
{
var engine = CipherUtilities.GetCipher("AES/ECB/PKCS7Padding");
engine.Init(false, new KeyParameter(pwBuffer.ToArray().Take(16).ToArray()));
var size = inputStream.Length;
var current = inputStream.Position;
var chunkSize = 1024 * 1024L;
var lastChunk = false;
await Task.Yield();
// Initialize DataReader and DataWriter for reliable reading and writing
// to a stream. Writing directly to a stream is unreliable.
using (var reader = new BinaryReader(inputStream))
using (var writer = new BinaryWriter(outputStream))
{
while (current < size)
{
if (size - current < chunkSize)
{
chunkSize = (uint)(size - current);
lastChunk = true;
}
var chunk = new byte[chunkSize];
reader.Read(chunk, 0, (int)chunkSize);
// The last chunk must call DoFinal() as it appends additional bytes
var processedBytes = lastChunk ? engine.DoFinal(chunk) : engine.ProcessBytes(chunk);
writer.Write(processedBytes);
current = inputStream.Position;
await Task.Yield();
}
await outputStream.FlushAsync();
}
}
}
return destFile;
}