You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

对称算法文件加解密:加密错误致解密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;
                        }
                    }
                }
            }
        }
    }
}

问题根源

  1. CBC模式状态未维护:AES-CBC是块链模式,每个加密块的输出会作为下一个块的IV。原代码每次加密缓冲区都使用初始IV,完全破坏了块链关系,导致加密数据不符合CBC模式规范,解密时无法正确处理。
  2. PKCS7填充错误:PKCS7填充是针对整个明文的最后一块进行的,原代码对每个缓冲区单独加密时都添加了填充,导致加密后的数据包含多段填充信息,解密时无法正确验证和移除填充,触发COM异常。
  3. 单次加密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();
    }
}

关键修复点说明

  1. 使用流式加密解密:通过CreateEncryptorTransform和CreateDecryptorTransform创建维护状态的加密/解密转换器,自动处理CBC模式的块链更新和PKCS7填充逻辑。
  2. 适配到.NET标准流:通过自定义CryptographicTransformAdapter将UWP的CryptographicTransform适配为.NET标准的ICryptoTransform,结合CryptoStream实现高效的流式处理,既支持大文件又保证加密正确性。
  3. 简化异步逻辑:使用Stream.CopyToAsync和FlushFinalBlockAsync简化异步流处理,避免手动分块的错误。

内容的提问来源于stack exchange,提问作者Power Code

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:09:50