这是一个非常常见的例外,但显然我找到的解决方案都没有解决我的问题.

我有一个加密和解密方法;我加密字符串并将其写入文件,然后从文件中读取字符串并解密(理论上).实际上,我得到了一个

CryptographicException:要解密的数据长度无效

在进程的解密端.

以下是完成所有工作的Main()种方法:

public static void Main()
{
    var filename = "test.encrypted";
    var plainText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry.";

    string password = "A better password than this";
    string salt = "Sodium Chloride";

    var padding = PaddingMode.Zeros; // I have tried every padding mode to no avail

    var encrypted = Encrypt<AesManaged>(plainText, password, salt, padding);
    File.WriteAllBytes(filename, encrypted);

    var fileBytes = File.ReadAllBytes(filename);
    var decrypted = Decrypt<AesManaged>(fileBytes, password, salt, padding);

    Console.ReadLine();
}

加密端:

static byte[] Encrypt<T>(string plainText, string password, string salt, PaddingMode padding)
    where T : Symmetric算法rithm, new()
{
    var saltBytes = Encoding.Unicode.GetBytes(salt);
    var derivedBytes = new Rfc2898DeriveBytes(password, saltBytes);

    using (var algorithm = new T())
    {
        algorithm.Padding = padding;

        var key = derivedBytes.GetBytes(algorithm.KeySize >> 3);
        byte[] iv = new byte[algorithm.BlockSize >> 3];
        RNGCryptoServiceProvider.Create().GetNonZeroBytes(iv);

        var transform = algorithm.CreateEncryptor(key, iv);


        using (MemoryStream buffer = new MemoryStream())
        using (CryptoStream cryptoStream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
        using (StreamWriter writer = new StreamWriter(cryptoStream, Encoding.Unicode))
        {
            writer.Write(plainText);
            writer.Flush();  
             
            // cryptoStream.FlushFinalBlock() is called after writer.Flush()
            // or as part of the Dispose().
            // Calling it here causes a "you can't call that twice" exception.

            // prepend IV to the data
            return iv.Concat(buffer.ToArray()).ToArray();
        }
    }
}

... 还有另一面


static byte[] Decrypt<T>(byte[] encryptedData, string password, string salt, PaddingMode padding)
      where T : Symmetric算法rithm, new()
{
    var saltBytes = Encoding.Unicode.GetBytes(salt);

    var derivedBytes = new Rfc2898DeriveBytes(password, saltBytes);

    using (var algorithm = new T())
    {
        algorithm.Padding = padding;

        var key = derivedBytes.GetBytes(algorithm.KeySize >> 3);

        //IV is at the beginning of the data
        var iv = encryptedData.Take(algorithm.BlockSize >> 3).ToArray();
        encryptedData = encryptedData.Skip(iv.Length).ToArray();

        var transform = algorithm.CreateDecryptor(key, iv);

        using (MemoryStream buffer = new MemoryStream())
        using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
        using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
        {
            writer.Write(encryptedData);
            writer.Flush();
            return buffer.ToArray();
        }
    }
}

推荐答案

Imo最简单的方法是遵循MS模式,例如here:

  • 对于加密:

    using (MemoryStream buffer = new MemoryStream())
    using (CryptoStream cryptoStream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
    {
        using (StreamWriter writer = new StreamWriter(cryptoStream, Encoding.Unicode))
        {
            writer.Write(plainText);
        }
        return iv.Concat(buffer.ToArray()).ToArray();
    }
    
  • 对于解密:

    using (MemoryStream buffer = new MemoryStream(encryptedData))
    using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Read))
    using (StreamReader reader = new StreamReader(stream, Encoding.Unicode))
    {
        return Encoding.Unicode.GetBytes(reader.ReadToEnd());
    }
    

使用此构造,Close()FlushFinalBlock()等的调用按正确顺序通过隐式Dispose()调用触发(有关详细信息,请参阅here).

旧的实现并非如此,它会导致不完整的加密和解密.此外,在解密过程中,密文被Unicode(更准确地说是UTF-16LE)编码损坏(这是发布异常的原因).


还要记住以下几点:

  • 如果稍后对解密的数据进行Unicode解码(很可能),则返回字符串比返回Decrypt()中的byte[](即return reader.ReadToEnd())更有效.
  • 至于填充,应该使用PKCS7填充而不是零填充,因为PKCS#7填充更可靠(相对于代码中的注释).
  • 使用StreamReader#.ReadToEnd()可以防止与breaking change in .NET 6相关的问题.

Csharp相关问答推荐

更改对象的旋转方向

始终保留数组中的最后N个值,丢弃最老的

. NET在上一个操作完成之前,在此上下文实例上启动了第二个操作

具有单一导航属性的EF核心一对多关系

最新的Mediatr和具有同步方法的处理程序Handle:并非所有代码路径都返回值"

如何让NLog停止写入冗余信息?

集合表达式没有目标类型

HttpRequestMessage.SetPolicyExecutionContext不会将上下文传递给策略

如何在用户在线时限制令牌生成?

正在寻找新的.NET8 Blazor Web应用程序.如何将.js添加到.razor页面?

异步任务调用程序集

使用CollectionView时在.NET Maui中显示数据时出现问题

在使用UserManager时,如何包含与其他实体的关系?

MudBlazor Textfield已禁用,但其验证工作正常

未在Windows上运行的Maui项目

错误:此版本的Visual Studio无法打开以下项目

如何在C#.NET桌面应用程序中动态更改焦点工具上的后退 colored颜色

无效的Zip文件-Zip存档

Avalonia MVVM数据模板

异步等待,如何在Windows窗体中使用它们?