liaquore
liaquore

Reputation: 397

RijndaelManaged "padding is invalid and cannot be removed"

I'm trying to create methods for very strong Rijndael 256 string encryption that I can use for passwords but I get an error saying Padding is invalid and cannot be removed. when I read the CryptoStream and get the decrypted string. Here are my encrypt and decrypt methods:

private string AES256EncryptString(string key, string plainText)
{
    try
    {
        using (RijndaelManaged rijndael = new RijndaelManaged())
        {
            rijndael.KeySize = 256;
            rijndael.BlockSize = 128;
            rijndael.Key = Encoding.UTF8.GetBytes(key);
            rijndael.GenerateIV();
            rijndael.Mode = CipherMode.CBC;
            rijndael.Padding = PaddingMode.PKCS7;
            ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, rijndael.IV);
            MemoryStream memoryStream = new MemoryStream();
            memoryStream.Write(rijndael.IV, 0, rijndael.IV.Length);
            CryptoStream crypoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            crypoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            crypoStream.FlushFinalBlock();
            crypoStream.Close();
            byte[] encryptedBytes = memoryStream.ToArray();
            memoryStream.Close();
            string encryptedText = Convert.ToBase64String(encryptedBytes);
            return encryptedText;
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
        return null;
    }
}

private string AES256DecryptString(string key, string encryptedText)
{
    try
    {
        using (RijndaelManaged rijndael = new RijndaelManaged())
        {
            rijndael.KeySize = 256;
            rijndael.BlockSize = 128;
            rijndael.Key = Encoding.UTF8.GetBytes(key);
            byte[] encryptedTextBytes = Encoding.UTF8.GetBytes(encryptedText);
            byte[] iv = new byte[16];
            Array.Copy(encryptedTextBytes, iv, iv.Length);
            rijndael.IV = iv;
            rijndael.Mode = CipherMode.CBC;
            rijndael.Padding = PaddingMode.PKCS7;
            ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV);
            MemoryStream memoryStream = new MemoryStream();
            byte[] encryptedTextWithoutIVBytes = new byte[encryptedTextBytes.Length - iv.Length];
            Array.Copy(encryptedTextBytes, 16, encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
            memoryStream.Write(encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
            StreamReader streamReader = new StreamReader(cryptoStream);
            string decryptedText = streamReader.ReadToEnd();
            cryptoStream.FlushFinalBlock();
            cryptoStream.Close();
            memoryStream.Close();
            return decryptedText;
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
        return null;
    }
}

As you can see I add the initialization vector before the encrypted string before adding the encrypted bit because I know that IVs should be random and I've seen this is a good strategy to use. I make sure to remove the IV before decrypting.

Is there a way to fix this without changing the padding mode (I've seen that PKCS7 padding is very secure)?

Upvotes: 0

Views: 243

Answers (1)

President James K. Polk
President James K. Polk

Reputation: 41958

Problems:

  1. You should use a proper password-based KDF for passwords and similar low-entropy keys. .NET has the Rfc2898DeriveBytes (PBKDF2) class to make this relatively easy.

  2. You are not base64 decoding your ciphertext in your decryptor. Instead of

    byte[] encryptedTextBytes = Encoding.UTF8.GetBytes(encryptedText);
    

    you should have

    byte[] encryptedTextBytes = Convert.FromBase64String(encryptedText);
    
  3. You need to reset the position of the MemoryStream after you populate it with ciphertext bytes. After

    memoryStream.Write(encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
    

    you need to insert

    memoryStream.Seek(0, SeekOrigin.Begin);
    

Upvotes: 1

Related Questions