1

I am following the MSDN Example of Rijndael Encryption, only that I would like to encrypt and return a stream.

The following does not work.

It throws no exception but after stepping through the code, the return value has no data.

        public static Stream EncryptStream(Stream plainStream, byte[] Key, byte[] IV)
        {

            var encrypted = new MemoryStream()

            // Create an RijndaelManaged object 
            // with the specified key and IV. 
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for encryption. 
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainStream);
                        }
                        msEncrypt.CopyTo(encrypted);
                    }
                }
            }
            return encrypted;


        }

I looked at the documentation for the Stream.Writer class, thinking that it has something to do with it not supporting writing to a Stream.

I noticed that there is an 'object' type parameter, so I am assuming it would work... Is that correct? If not, how do I do it?

I pass a FileStream to it, by the way. Stepping through the code, plainStream does contain data.

Matthew Haugen
  • 11,855
  • 5
  • 34
  • 52
user919426
  • 6,744
  • 9
  • 41
  • 73
  • @AlexeiLevenkov I had not asked about "setting position". If that's where the solution lies, I couldnt have known that it had something to do with "setting position" – user919426 Mar 13 '15 at 05:05
  • @AlexeiLevenkov, it actually doesnt solve the issue. If you ignore the CopyTo() call and look at msEncrypt (a Stream), it does not contain data. Try it. – user919426 Mar 13 '15 at 05:10
  • @user919426 what if you perform `msEncrypt.CopyTo(encrypted);` after disposing `csEncrypt`, still gives you issues? – mishamosher Mar 13 '15 at 05:25
  • how about " plainStream.CopyTo(swEncrypt);" ? tell me if this works or not. – lincoln Mar 13 '15 at 05:29
  • Side note - I don't believe that "It throws no exception" is true as you are closing streams and than try to copy. – Alexei Levenkov Mar 13 '15 at 06:43

2 Answers2

2

Here are some sample function to encrypt and decrypt from and to streams (replace the algo by the one you prefer):

public static void Decrypt(Stream input, Stream output, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algo = SymmetricAlgorithm.Create()) // Creates the default implementation, which is RijndaelManaged. 
    {
        using (CryptoStream stream = new CryptoStream(input, algo.CreateDecryptor(key, iv), CryptoStreamMode.Read))
        {
            byte[] bytes = new byte[16];
            int read;
            do
            {
                read = stream.Read(bytes, 0, bytes.Length);
                output.Write(bytes, 0, read);
            }
            while (read > 0);
        }
    }
}

public static void Encrypt(Stream input, Stream output, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algo = SymmetricAlgorithm.Create()) //Creates the default implementation, which is RijndaelManaged. 
    {
        using (CryptoStream stream = new CryptoStream(output, algo.CreateEncryptor(key, iv), CryptoStreamMode.Write))
        {
            byte[] bytes = new byte[16];
            int read;
            do
            {
                read = input.Read(bytes, 0, bytes.Length);
                stream.Write(bytes, 0, read);
            }
            while (read > 0);
        }
    }
}

You can use them with any output stream. If you want to write to a large output stream, you can use that output stream directly (for example a FileStream or ASP.NET Response.OutputStream, etc.), you should not use an intermediary MemoryStream which will consume memory for no real purpose.

That being said, if you really want to use a MemoryStream, you would do it like this:

MemoryStream output = new MemoryStream();
Encrypt(input, output, key, iv);
output.Position = 0; // rewind the stream, so you can use it from the beginning
Simon Mourier
  • 117,251
  • 17
  • 221
  • 269
  • Thanks Simon. I have one quick question on why you chose a what I assume is a buffer( byte[] bytes = byte[16]) Is there any relation of that to my actual data buffer length? – user919426 Mar 13 '15 at 11:13
  • oh, that's something I carried over from an old piece of code. In fact you can/should put a higher value for better performance (like 65536 for example if your stream is big). Make sure it's not bigger than 85K though (http://stackoverflow.com/questions/8951836/why-large-object-heap-and-why-do-we-care) – Simon Mourier Mar 13 '15 at 13:05
  • An exception is thrown when it hits the disposing brace of `using (CryptoStream `. It says "Length of the data to encrypt is invalid." Will try get it working – user919426 Mar 14 '15 at 04:55
  • I removed the `using` statement for the CryptoStream, as it was causing the the stream to be closed. Now working! – user919426 Mar 14 '15 at 09:15
  • yeah, there are some subtleties with CryptoStream ..., for example: http://blogs.msdn.com/b/winsdk/archive/2009/11/13/do-you-need-to-explicitly-call-cryptostream-close-to-close-your-cryptostream-object-after-you-are-done-using-it.aspx – Simon Mourier Mar 14 '15 at 09:19
1

Use stream level copy to copy content of one stream to another OR use corresponding pair of Reader/Writer (like TextReader/TextWriter) - if you mix than most likely you get wrong result. I.e. stream level copy:

 plainStream.CopyTo(csEncrypt);

In addition to actually writing data to the encrypted stream (instead of type name of plainStream which you get due to StreamWrite.Write(Object) call) you should use MemoryStream.ToArray to copy resulting content - otherwise you are getting "object disposed exception".

Copy code should look like following instead of msEncrypt.CopyTo(encrypted);

var bytes = msEncrypt.ToArray();
encrypted.Write(bytes, 0, bytes.Length);
Alexei Levenkov
  • 94,391
  • 12
  • 114
  • 159
  • Thanks for the detailed explanation. Marked Simon's as the answer because of the implementation detail, but taking nothing away from your good explanation – user919426 Mar 13 '15 at 11:07