【问题标题】:Rijndael padding or length is invalidRijndael 填充或长度无效
【发布时间】:2012-03-05 22:12:40
【问题描述】:

我正在尝试使用 Rijndael 或 Aes 以及下面的代码来加密/解密字符串。

public class Crypto
{
    private const string defaultVector = "asdfg123456789";
    private const CipherMode cipherMode = CipherMode.CBC;
    //Have tried PaddingMode.ISO10126, PaddingMode.None, and PaddingMode.PKCS7
    private const PaddingMode paddingMode = PaddingMode.ISO10126;
    private const int iterations = 2;
    private static Rijndael GetCrypto(string passphrase)
    {
        var crypt = Rijndael.Create();
        crypt.Mode = cipherMode;
        crypt.Padding = paddingMode;
        crypt.BlockSize = 256;
        crypt.KeySize = 256;
        crypt.Key =
            new Rfc2898DeriveBytes(passphrase, Encoding.Unicode.GetBytes(defaultVector), iterations).GetBytes(32);
        crypt.IV = new Rfc2898DeriveBytes(passphrase, Encoding.Unicode.GetBytes(defaultVector), iterations).GetBytes(32);
        return crypt;
    }
    public static string Encrypt(string plainText, string passphrase)
    {
        byte[] clearData = Encoding.Unicode.GetBytes(plainText);
        byte[] encryptedData;
        var crypt = GetCrypto(passphrase);
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, crypt.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearData, 0, clearData.Length);
                //cs.FlushFinalBlock(); //Have tried this active and commented with no change.
            }
            encryptedData = ms.ToArray();
        }
        //Changed per Xint0's answer.
        return Convert.ToBase64String(encryptedData);
    }
    public static string Decrypt(string cipherText, string passphrase)
    {
        //Changed per Xint0's answer.
        byte[] encryptedData = Convert.FromBase64String(cipherText);
        byte[] clearData;
        var crypt = GetCrypto(passphrase);
        using (var ms = new MemoryStream())
        {
                using (var cs = new CryptoStream(ms, crypt.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(encryptedData, 0, encryptedData.Length);
                    //I have tried adding a cs.FlushFinalBlock(); here as well.
                }
                clearData = ms.ToArray();
        }
        return Encoding.Unicode.GetString(clearData);
    }
}

//编辑:我已经根据下面Xint0 的回答将Unicode 调用更改为Convert.ToBase64String。

在 cs.Write in Decrypt 方法中,我收到“填充无效且无法删除”的错误。

我尝试将填充设置为 PaddingMode.None,但我得到“要加密的数据长度无效”。在 cs.Write 的 Encrypt 方法中。

我看过这些,但他们所说的似乎都不起作用。

Padding is invalid and cannot be removed

Padding is invalid and cannot be removed?

堆栈跟踪显示 System.Security.CryptographicException 来自 RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)。

【问题讨论】:

    标签: c# aes rijndael encryption-symmetric


    【解决方案1】:

    我花了很多时间来查找导致 CryptographicException 的原因,我也在谷歌上搜索,包括 Stackoverflow。
    这是一个愚蠢的错误(在使用复制粘贴编程时经常出现),如下所示:

    它从 CryptoStream 的实例中抛出方法 FlushFinalBlock()

    查看错误代码:

    CryptoStream cs = new CryptoStream(ms, rj.CreateDecryptor(rj.Key, rj.IV), CryptoStreamMode.Write);
    

    我用它来加密,所以你可以看到 CryptoStreamMode.Write 但在同一条指令中,我创建的是解密器而不是加密器(请参阅构造函数中的第二个参数)。

    小心检查,以免浪费您宝贵的时间;)

    问候
    布罗内克

    【讨论】:

      【解决方案2】:

      我看到两个问题:

      1. 在调用 ms.ToArray() 之前,您不会刷新和关闭流。将其更改为:

        ...
        using (var cs = new CryptoStream(ms, crypt.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(clearData, 0, clearData.Length);
            cs.FlushFinalBlock();
            cs.Close();
        }
        
        ms.Close();
        encryptedData = ms.ToArray();
        ...
        
      2. Encrypt 中,生成的字节数组encryptedData 不是 Unicode 字符串,但您正在使用Unicode 编码器从字节数组中获取字符串。而不是在Encrypt 中使用System.Convert.ToBase64String(),在Decrypt 中使用System.Convert.FromBase64String()

      Encrypt 做:

      return System.Convert.ToBase64String(encryptedData);
      

      Decrypt 做:

      byte[] encryptedData = System.Convert.FromBase64String(cipherText);
      

      编辑

      最大的问题是Encrypt的返回值。加密 Unicode 字符串的字节表示的结果是不是 Unicode 字符串的字节表示。您不应将encryptedData 的值与Encoding.Unicode.GetString() 一起使用来获取加密数据的字符串表示形式。使用System.Convert.ToBase64String() 获取加密数据的字符串表示。请参阅Encoding Class MSDN 文档中的备注部分。

      编辑 2

      请注意,Rijndael 不完全是 AES,如果您与 AES 互操作,则块大小应始终为 128 位,与密钥大小无关。详情可以阅读here

      【讨论】:

      • 我已经尝试了添加 cs.Close() 和 cs.FlushFinalBlock() 的所有排列。堆栈跟踪显示来自 CryptoStream 调用的 System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) 的错误。 FlushFinalBlock()。当 CryptoStream 关​​闭/处置时,也会调用 FlushFinalBlock。所以我在这里不知所措。
      • @PMontgomery 您是否将Encoding.Unicode.GetString(encryptedBytes) 替换为System.Convert.ToBase64String(encryptedData)?这是你最大的问题。
      • 是的,我已经进行了这些更改并编辑了原始问题以反映。它没有改变错误。
      • 感谢您对此提供的所有帮助。我相信找到了问题。这实际上是上游发生的事情,其中​​密码在加密/解密调用之间发生了变化。你能猜出哪对调用现在有一个额外的单元测试来表明它不是有问题的代码位吗?
      • @PMontgomery 请注意,Rijndael 并不完全是 AES。在 AES 中,块大小始终为 128 位,与密钥大小无关。
      【解决方案3】:

      我有一个类似的问题,解密方法的问题是初始化一个空的内存流。当我用这样的密文字节数组初始化它时它起作用了:

      MemoryStream ms = new MemoryStream(cipherText);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-26
        • 2016-11-15
        • 1970-01-01
        • 2013-04-28
        • 1970-01-01
        • 1970-01-01
        • 2012-05-24
        相关资源
        最近更新 更多