逐块解密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;
}