2

I am trying to encrypt/decrypt a string using eith Rijndael or Aes and the code below.

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);
    }
}

//Edits: I have changed over the Unicode calls to Convert.ToBase64String per Xint0's answer below.

On the cs.Write in Decrypt method, I am getting the error that "Padding is invalid and cannot be removed."

I have tried setting the padding to PaddingMode.None but I get "Length of the data to encrypt is invalid." on the cs.Write in the Encrypt method.

I've looked at these and nothing they've said seems to work.

Padding is invalid and cannot be removed

Padding is invalid and cannot be removed?

Stack trace shows System.Security.CryptographicException is coming from RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast).

Community
  • 1
  • 1
PMontgomery
  • 424
  • 4
  • 17

3 Answers3

3

I have spend a lot of my time for finding what was causing CryptographicException and I was googling too including Stackoverflow.
It was a stupid mistake (as often when programming with copy-paste) as follow:

It was throwing on method FlushFinalBlock() from instance of CryptoStream.

Look at WRONG code:

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

I used it to encrypt so you can see CryptoStreamMode.Write but in the same instruction I was creating decryptor instead of encryptor (see second parameter in the constructor).

Be careful and check it to avoid wasting your precious time ;)

Regards
Bronek

Bronek
  • 10,722
  • 2
  • 45
  • 46
2

I see two problems:

  1. You are not flushing and closing the streams before calling ms.ToArray(). Change it to:

    ...
    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. In Encrypt the resulting byte array encryptedData is NOT a Unicode string, yet you are using a Unicode encoder to get a string from the byte array. Instead of that use System.Convert.ToBase64String() in Encrypt and System.Convert.FromBase64String() in Decrypt.

In Encrypt do:

return System.Convert.ToBase64String(encryptedData);

In Decrypt do:

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

EDIT

The biggest problem is the return value of Encrypt. The result of encrypting the byte representation of a Unicode string is NOT a byte representation of a Unicode string. You should not use the value of encryptedData with Encoding.Unicode.GetString() to get a string representation of the encrypted data. Use System.Convert.ToBase64String() to get a string representation of the encrypted data. Please see the Remarks section in the Encoding Class MSDN Documentation.

EDIT 2

Note that Rijndael is not exactly AES, if you are interoperating with AES the block size should always be 128-bits, independent of the key size. For details you can read about it here.

Xint0
  • 5,221
  • 2
  • 27
  • 29
  • I have tried every permutation of adding the cs.Close() and cs.FlushFinalBlock(). The stack trace shows the error coming from System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) which is called by CryptoStream.FlushFinalBlock(). FlushFinalBlock is also called when the CryptoStream is Closed/Disposed. So I am at a loss here. – PMontgomery Mar 06 '12 at 15:24
  • @PMontgomery Have you replaced `Encoding.Unicode.GetString(encryptedBytes)` with `System.Convert.ToBase64String(encryptedData)`? This is your biggest problem. – Xint0 Mar 06 '12 at 17:45
  • Yes, I have made those changes and edited the original question to reflect. It has made no change in the error. – PMontgomery Mar 06 '12 at 19:41
  • thanks for all your help on this. I believe found the issue. It is actually something happening upstream where the passphrase was getting changed between the Encrypt/Decrypt calls. Can you guess what pair of calls has an additional unit test wrapped around it now to show that it *isn't* the problematic bit of code? – PMontgomery Mar 06 '12 at 19:58
  • @PMontgomery please be aware that Rijndael is not exactly AES. In AES the block size is always 128 bits, independent of the key size. – Xint0 Mar 07 '12 at 00:29
0

I had a similar problem, the issue in decrypt method was initializing an empty memory stream. when it worked when I initialized it with the cipher text byte array like this:

MemoryStream ms = new MemoryStream(cipherText);
Mina Wissa
  • 10,923
  • 13
  • 90
  • 158