2

I am trying to encrypt a Generic Stream in C#. Although the program has no problems, the encryption and decryption return blank when converted to strings. Any help is appreciated.

    public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
        {
            byte[] encryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.Close();
                    }
                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
        {
            byte[] decryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                        cs.Close();
                    }
                    decryptedBytes = ms.ToArray();
                }
            }

            return decryptedBytes;
        }

        private void Encrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Encrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        private void Decrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Decrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        static void Main(string[] args)
        {

            Program obj = new Program();
            var message = new MemoryStream();
            var cipher = new MemoryStream();
            string tmp = "This is a test if the encryption is working!";

            StreamWriter sw = new StreamWriter(message);
            sw.Write(tmp);

            obj.Encrypt(message, cipher, "password");

            cipher.Position = 0;
            message = new MemoryStream();

            obj.Decrypt(cipher, message, "password");

            using (var memoryStream = new MemoryStream())
            {
                message.CopyTo(memoryStream);
                var bytesdecrypt = memoryStream.ToArray();
                string result = Encoding.UTF8.GetString(bytesdecrypt);
                Console.WriteLine(result);
                Console.ReadLine();
            }


        }
    }
}

The problem is probably when I am reading and writing from and to the streams.

  • 2
    Check this out: https://stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string/10366194#10366194 – Frank Bryce Jun 07 '16 at 20:59
  • 1
    What do you mean by "blank"? How are you checking whether there is something there? – Artjom B. Jun 07 '16 at 21:41
  • Johnny D. Here is a more relevant link for your particular problem: http://stackoverflow.com/questions/22145836/memorystream-copyto-not-working – mikey Jun 07 '16 at 21:56
  • Once I decrypt, I get an empty string. –  Jun 08 '16 at 13:44
  • So the encryption is non-blank, right? – Artjom B. Jun 08 '16 at 13:46
  • The problem is in the stream IO, the encryption is pretty standard –  Jun 08 '16 at 13:47
  • Working example using a string and a passphrase: https://www.selamigungor.com/post/7/encrypt-decrypt-a-string-in-csharp - doesn't address the stream issue where the position simply needed to be reset. – vapcguy Feb 12 '20 at 19:19

1 Answers1

4

There are many problems with this code.

  1. The reason why nothing is decrypted is because you forgot to reset the message stream before doing message.CopyTo(memoryStream), because CopyTo works from the current position and you haven't changed the position after decryption.

    You could reset it with

    message.Position = 0;
    
  2. If arbitrary data is encrypted, AES with some mode of operation like CBC is not enough. We generally need some kind of padding scheme. In C# the default scheme is PKCS#7 padding. Unambiguous padding is always added even when the plaintext is already a multiple of the block size. In those cases a full padding block is added.

    Now the problem is that you're reading 2048 byte chunks during encryption and decryption, but the encryption produces 2064 byte ciphertext chunks which must be read as such during decryption. This is a simple fix, but it would be better to use streams all the way instead of encrypting such separate chunks.

  3. You're invoking Rfc2898DeriveBytes for every 2048 byte chunk, but it never changes. Either introduce randomness, but actually using a random salt and random IV, or cache the key. (random salt and random IV are still necessary to reach semantic security)

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Thanks @Artjom B.! –  Jun 08 '16 at 17:58
  • Example with PKCS#7 padding would've been appreciated. Nice to criticize, but without offering code to show a proof of concept, it's not very helpful. Example of how to randomize the salt would've been useful, too. – vapcguy Feb 12 '20 at 19:23