对称算法文件加解密:加密错误致解密Com异常的排查修复
大文件AES-CBC加密解密的问题排查与修复
问题描述
使用对称算法处理文件加密解密,小文件处理无问题,但大文件(100MB、1GB、5GB等)需特殊处理。通过ChatGPT生成EncryptFileAsync和DecryptFileAsync方法后,加密方法可正常读取并加密大文件写入磁盘,但解密时在CryptographicEngine.Decrypt行抛出COM异常,即使3KB小文件也存在该问题。确认加密逻辑错误导致解密失败,需排查加密方法问题并修复。
原代码
public static async Task EncryptFileAsync(string inputFile, string outputFile) { var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); var keyMaterial = CryptographicBuffer.GenerateRandom(provider.BlockLength); var iv = CryptographicBuffer.GenerateRandom(provider.BlockLength); var key = provider.CreateSymmetricKey(keyMaterial); const int bufferSize = 4096; // Choose an appropriate buffer size var file = await StorageFile.GetFileFromPathAsync(inputFile); using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read)) { using (IInputStream inputStream = stream.GetInputStreamAt(0)) { using (DataReader dataReader = new DataReader(inputStream)) { using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) { while (stream.Position < stream.Size) { dataReader.InputStreamOptions = InputStreamOptions.Partial; uint bytesRead = await dataReader.LoadAsync(bufferSize); if (bytesRead > 0) { // Convert the buffer to IBuffer IBuffer buffer = dataReader.ReadBuffer(bytesRead); IBuffer bufferEncrypt = CryptographicEngine.Encrypt(key, buffer, iv); var byteArray = bufferEncrypt.ToArray(); await fs.WriteAsync(byteArray, 0, byteArray.Length); // Your processing logic with the IBuffer goes here } else { // Break the loop if no more bytes can be read break; } } } } } } // Save the key and iv for later decryption SaveKeyAndIV(keyMaterial, iv); } public static async Task DecryptFileAsync(string inputFile, string outputFile) { // Load the key and iv used for encryption var (keyMaterial, iv) = LoadKeyAndIV(); var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); var key = provider.CreateSymmetricKey(keyMaterial); const int bufferSize = 4096; // Choose an appropriate buffer size var file = await StorageFile.GetFileFromPathAsync(inputFile); using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read)) { using (IInputStream inputStream = stream.GetInputStreamAt(0)) { using (DataReader dataReader = new DataReader(inputStream)) { using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) { while (stream.Position < stream.Size) { dataReader.InputStreamOptions = InputStreamOptions.Partial; uint bytesRead = await dataReader.LoadAsync(bufferSize); if (bytesRead > 0) { // Convert the buffer to IBuffer IBuffer buffer = dataReader.ReadBuffer(bytesRead); IBuffer bufferDecrypt = CryptographicEngine.Decrypt(key, buffer, iv); // Convert the decrypted buffer to byte array var decryptedBytes = bufferDecrypt.ToArray(); await fs.WriteAsync(decryptedBytes, 0, decryptedBytes.Length); // Your processing logic with the decrypted data goes here } else { // Break the loop if no more bytes can be read break; } } } } } } }
问题根源
- CBC模式状态未维护:AES-CBC是块链模式,每个加密块的输出会作为下一个块的IV。原代码每次加密缓冲区都使用初始IV,完全破坏了块链关系,导致加密数据不符合CBC模式规范,解密时无法正确处理。
- PKCS7填充错误:PKCS7填充是针对整个明文的最后一块进行的,原代码对每个缓冲区单独加密时都添加了填充,导致加密后的数据包含多段填充信息,解密时无法正确验证和移除填充,触发COM异常。
- 单次加密API不适合流式处理:
CryptographicEngine.Encrypt是针对完整数据块的单次加密API,不能用于分块流式加密,必须使用维护状态的CryptographicTransform来处理流式数据。
修复后的代码
public static async Task EncryptFileAsync(string inputFile, string outputFile) { var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); var keyMaterial = CryptographicBuffer.GenerateRandom(provider.BlockLength); var iv = CryptographicBuffer.GenerateRandom(provider.BlockLength); var key = provider.CreateSymmetricKey(keyMaterial); // 创建流式加密转换器,自动维护CBC状态和PKCS7填充 var encryptTransform = provider.CreateEncryptorTransform(key, iv); var inputFileObj = await StorageFile.GetFileFromPathAsync(inputFile); using var inputStream = await inputFileObj.OpenStreamForReadAsync(); using var outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true); using var cryptoStream = new CryptoStream(outputStream, encryptTransform.AsManagedTransform(), CryptoStreamMode.Write); // 流式复制并加密 await inputStream.CopyToAsync(cryptoStream); await cryptoStream.FlushFinalBlockAsync(); // 保存密钥和初始IV SaveKeyAndIV(keyMaterial, iv); } public static async Task DecryptFileAsync(string inputFile, string outputFile) { var (keyMaterial, iv) = LoadKeyAndIV(); var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); var key = provider.CreateSymmetricKey(keyMaterial); // 创建流式解密转换器,自动维护CBC状态和PKCS7填充验证 var decryptTransform = provider.CreateDecryptorTransform(key, iv); var inputFileObj = await StorageFile.GetFileFromPathAsync(inputFile); using var inputStream = await inputFileObj.OpenStreamForReadAsync(); using var outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true); using var cryptoStream = new CryptoStream(inputStream, decryptTransform.AsManagedTransform(), CryptoStreamMode.Read); // 流式读取并解密 await cryptoStream.CopyToAsync(outputStream); } // 辅助扩展方法:将CryptographicTransform转换为ICryptoTransform供CryptoStream使用 public static ICryptoTransform AsManagedTransform(this CryptographicTransform transform) { return new CryptographicTransformAdapter(transform); } // 适配CryptographicTransform到ICryptoTransform的包装类 private class CryptographicTransformAdapter : ICryptoTransform { private readonly CryptographicTransform _transform; public CryptographicTransformAdapter(CryptographicTransform transform) { _transform = transform; } public int InputBlockSize => (int)_transform.InputBlockSize; public int OutputBlockSize => (int)_transform.OutputBlockSize; public bool CanTransformMultipleBlocks => _transform.CanTransformMultipleBlocks; public bool CanReuseTransform => _transform.CanReuseTransform; public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { var inputBufferIBuffer = CryptographicBuffer.CreateFromByteArray(inputBuffer.AsSpan(inputOffset, inputCount)); var outputBufferIBuffer = _transform.TransformBlock(inputBufferIBuffer); outputBufferIBuffer.CopyTo(outputBuffer, outputOffset); return (int)outputBufferIBuffer.Length; } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { var inputBufferIBuffer = CryptographicBuffer.CreateFromByteArray(inputBuffer.AsSpan(inputOffset, inputCount)); var outputBufferIBuffer = _transform.TransformFinalBlock(inputBufferIBuffer); return outputBufferIBuffer.ToArray(); } public void Dispose() { _transform.Dispose(); } }
关键修复点说明
- 使用流式加密解密:通过
CreateEncryptorTransform和CreateDecryptorTransform创建维护状态的加密/解密转换器,自动处理CBC模式的块链更新和PKCS7填充逻辑。 - 适配到.NET标准流:通过自定义
CryptographicTransformAdapter将UWP的CryptographicTransform适配为.NET标准的ICryptoTransform,结合CryptoStream实现高效的流式处理,既支持大文件又保证加密正确性。 - 简化异步逻辑:使用
Stream.CopyToAsync和FlushFinalBlockAsync简化异步流处理,避免手动分块的错误。
内容的提问来源于stack exchange,提问作者Power Code
相关产品推荐
相关产品推荐

