AES 分块解密在末尾添加随机字节

AES decrypt in chunks adds random bytes at the end

我有一个使用 AES 加密的非常大的 csv 文件。加密的代码

using var aes = new AesCryptoServiceProvider();

aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = initializationVector;

using var memoryStream = new MemoryStream();

var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

cryptoStream.Write(data, 0, data.Length);

cryptoStream.Flush();

稍后保存到文件中。在解密端,我试图以块的形式解密它,例如

using var sourceStream = File.OpenRead(path_to_encrypted_file);

using var aes = new AesCryptoServiceProvider();

aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = iv;

using (var fs = File.Create(path_to_decrypted_file))
using (var cryptoStream = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Write)
{
    var dataBuffer = new byte[81290];
    int read;

    while ((read = await sourceStream.ReadAsync(dataBuffer)) != 0)
    {
        ReadOnlyMemory<byte> buffer = dataBuffer.AsMemory().Slice(0, read);
        await cryptoStream.WriteAsync(buffer);
        await cryptoStream.FlushAsync();
    }
}

文件已解密,但是,我在文件末尾看到一些随机字节和空行

我解密的方式有问题吗?

我首先要调查几个潜在问题,至少在现有提供的代码中是这样。可能还有更多取决于您如何生成初始数据字节数组、如何生成密钥、如何将加密流写入磁盘等。

  1. 您正在使用 ECB,而您几乎肯定不应该。它对你的静脉注射也没有任何作用。根据应用考虑 CBC 或 GCM。

  2. 您没有使用填充模式。除非您的数据完全包含在块大小内,否则您可能会丢失一些数据,这可能会导致最后出现乱码。

  3. 你不显示原始加密流处理,你只显示Flush()。根据其处理方式,它可能不会调用 CryptoStream 的 FlushFinalBlock() 方法,这很重要。鉴于缺少填充模式,如果你添加它,你可能会突然看到自己在这里有一个例外提醒你 The input data is not a complete block. 由于 #2 直到你换掉它。

感谢 我根据答案和评论中的建议重新实现了 encrypt/decrypt。

一点背景知识——我需要一个解决方案,在客户端机器上进行加密(与互联网断开连接),一旦加密的文件上传到 blob 存储,稍后在云中进行解密。

我想要混合加密,其中密钥是 RSA 加密的,数据是 AES。 所以客户端上的文件内容:

  • RSA 加密密钥
  • RSA 加密 IV(IV 的 RSA 加密不是必需的 AFAIK)
  • AES 加密数据

这是最终实现:

// Local
var localRsa = RSA.Create();

localRsa.ImportRSAPublicKey(
    Convert.FromBase64String(public_key),
    out var _);

var localAes = Aes.Create();
localAes.GenerateKey();
localAes.GenerateIV();

localAes.Mode = CipherMode.CBC;
localAes.Padding = PaddingMode.PKCS7;

using (var dataStream = File.OpenRead(file_to_encrypt))
using (var secretFileStream = File.Create(encrypted_file))
{
    await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.Key, RSAEncryptionPadding.OaepSHA256));
    await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.IV, RSAEncryptionPadding.OaepSHA256));

    using (var cryptoStream = new CryptoStream(secretFileStream, localAes.CreateEncryptor(localAes.Key, localAes.IV), CryptoStreamMode.Write))
    {
        await dataStream.CopyToAsync(cryptoStream);
    }
}

以及解密片:

// Cloud
var cloudRsa = RSA.Create();

cloudRsa.ImportRSAPrivateKey(
    Convert.FromBase64String(private_key),
    out var _);

var cloudAes = Aes.Create();

cloudAes.Mode = CipherMode.CBC;
cloudAes.Padding = PaddingMode.PKCS7;

using (var secretFileStream = File.OpenRead(encrypted_file))
{
    var keyBuffer = new byte[256];
    await secretFileStream.ReadAsync(keyBuffer, 0, keyBuffer.Length);

    cloudAes.Key = cloudRsa.Decrypt(keyBuffer, RSAEncryptionPadding.OaepSHA256);

    var ivBuffer = new byte[256];
    await secretFileStream.ReadAsync(ivBuffer, 0, keyBuffer.Length);

    cloudAes.IV = cloudRsa.Decrypt(ivBuffer, RSAEncryptionPadding.OaepSHA256);

    secretFileStream.Position = 512;

    using (var plainTextStream = File.Create(decrypted_file))
    {
        using (var cryptoStream = new CryptoStream(secretFileStream, cloudAes.CreateDecryptor(cloudAes.Key, cloudAes.IV), CryptoStreamMode.Read))
        {
            await cryptoStream.CopyToAsync(plainTextStream);
        }
    }
}