Rijndael填充或长度无效

时间:2012-03-05 22:12:41

标签: c# aes rijndael encryption-symmetric

我正在尝试使用eith 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。

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

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

我看过这些,他们所说的似乎没什么用。

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)。

3 个答案:

答案 0 :(得分:3)

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

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

查看错误代码:

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

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

小心并检查以避免浪费宝贵的时间;)

问候
布罗尼斯瓦夫

答案 1 :(得分:1)

我看到两个问题:

  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 NOT 一个Unicode字符串,但您使用Unicode编码器从字节数组中获取字符串。而不是使用System.Convert.ToBase64String()中的EncryptSystem.Convert.FromBase64String()中的Decrypt

  3. Encrypt执行:

    return System.Convert.ToBase64String(encryptedData);
    

    Decrypt执行:

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

    修改

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

    编辑2

    请注意,Rijndael不完全是AES,如果您与AES互操作,则块大小应始终为128位,与密钥大小无关。有关详细信息,请参阅here

答案 2 :(得分:0)

我遇到了类似的问题,解密方法中的问题是初始化一个空的内存流。当我使用密文文本字节数组初始化它时它工作,如下所示:

MemoryStream ms = new MemoryStream(cipherText);