0

I am trying to implement an in-memory AESManaged encrypt/decrypt. The code here is based on this:

Encrypting/Decrypting large files (.NET)

The encrypt part seems to work, that is, there are no exceptions. But the decrypt part throws an "index was outside the bounds of the array" error.

In earlier code the transform is initialize like this:

 aes = new AesManaged();
 aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
 aes.KeySize = aes.LegalKeySizes[0].MaxSize;            
 Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(Key, salt, 1);
 aes.Key = key.GetBytes(aes.KeySize / 8);
 aes.IV = key.GetBytes(aes.BlockSize / 8);
 aes.Mode = CipherMode.CBC;
 transform = aes.CreateDecryptor(aes.Key, aes.IV);


void AESDecrypt(ref byte[] inB)
{
    using (MemoryStream destination = new MemoryStream(inB, 0, inB.Length))
    {
        using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
            {
                try
                {
                    using (MemoryStream source = new MemoryStream(inB, 0, inB.Length))
                    {
                        if (source.CanWrite==true)
                        {
                            source.Write(inB, 0, inB.Length);
                            source.Flush(); //<<inB is unchanged by the write
                        }
                    }
                }
                catch (CryptographicException exception)
                {
                    if (exception.Message == "Padding is invalid and cannot be removed.")
                        throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
                    else
                        throw;
                }
            }
        } <====At this point I get an IndexOutofBounds exception.
    }
}

It seems that the offending line may be: using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))

Community
  • 1
  • 1
Ron
  • 2,435
  • 3
  • 25
  • 34
  • And what have you tried? Like *debugging* your *own* code? – Ondrej Tucny Nov 15 '13 at 22:35
  • yes, not sure what you are getting at. – Ron Nov 16 '13 at 15:37
  • `if (source.CanWrite==true)`. And what if CanWrite is false? Then you just write nothing? Better to just let it fail if you do not handle the case anyway. – usr Nov 16 '13 at 16:42
  • Regarding your actual problem, the data you are sending in is wrong. Either wrong key, IV or bytes. So show the encryption as well. Btw, anyone can modify your encrypted data without the decryption noticing. You should use authenticated encryption (AES-GCM is pretty failsafe). – usr Nov 16 '13 at 16:43
  • Also, the IV should be unique for each encryption. You got lucky using CBC, but for other modes the same IV for different data destroys security 100%. – usr Nov 16 '13 at 16:45

1 Answers1

1

You aren't feeding the CryptoStream any data, and it needs some because it is trying to remove padding. Try commenting out the entire try/catch block of source and you'll get the same error.

The CryptoStream was left empty, and yet you are asking it to read padding. After the "new AesManaged()" line, add this: aes.Padding = PaddingMode.None. Now your code will work, although it won't decrypt anything. Since you are feeding CryptoStream nothing, and not asking it to read any padding, it no longer complains. It does nothing. You have a bug in that you aren't giving the ciphertext to the CryptoStream.

Try this instead of the MemoryStream for source:

using (BinaryWriter source = new BinaryWriter(cryptoStream))
{
    source.Write(inB, 0, inB.Length);
}

The CryptoStream is now involved, and it will receive inB to decrypt.

You may have a problem with your handling of padding. As your code is written (fixing the curly brace typo), you are asking the decryptor to strip padding, but you don't trim your output array (ref byte[] inB) so how do you know how much data is returned? It will always return the same length as the input, but only having overwritten the amount decrypted.

Here is some sample data:

Try a key of 32 zero bytes, and an IV of 16 zero bytes:

aes.Key = new byte[32];
aes.IV = new byte[16];

and decrypt this ciphertext as inB:

byte[] inB = { 0xf9, 0x14, 0x32, 0x2a, 0x7a, 0x35, 0xf9, 0xef, 0x27, 0x98, 0x1a, 0x86, 0xe2, 0x80, 0x5e, 0x9b };

If you do not set Padding.None, then you'll see my original plaintext "Hello" overwriting only the first five bytes of inB. The remaining 11 bytes are unchanged. The padding was removed (the default) and was not written to the destination stream.

Now set Padding.None and try it. Since I did pad the data, you will see destination now contains "Hello" followed by 11 bytes of value 11 -- the padding. Since the padding was not removed this time, you see it written to the output.

Also, as usr commented, the IV should be unique each time you encrypt with the key. You are deriving the same IV and key each time. If this key is only used once, that's fine. If the same key is used more than once, this is a mistake. The IV should be unique. It can be sent in the clear -- it does not need to be kept secret.

Jim Flood
  • 8,144
  • 3
  • 36
  • 48