liaquore
liaquore

Reputation: 397

AES 256 encryption can't get IV from byte array correctly

I'm trying to create a handler class for AES 256 encryption, and I saw that it is apparently secure to use a different IV for each encryption, and to put it at the beginning of the byte array. So I tried to add that but it can't get it correctly. Here is my code:

using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace EncryptionTest
{
    public class Aes256Handler
    {
        private byte[] key;

        public Aes256Handler(byte[] key)
        {
            this.key = key;
        }

        public string EncryptString(string plaintext)
        {
            return Encoding.UTF8.GetString(Encrypt(Encoding.UTF8.GetBytes(plaintext)));
        }

        public string DecryptString(string encryptedtext)
        {
            return Encoding.UTF8.GetString(Decrypt(Encoding.UTF8.GetBytes(encryptedtext)));
        }

        public byte[] Encrypt(byte[] bytes)
        {
            if (bytes == null || bytes.Length < 1)
            {
                throw new ArgumentException("Invalid bytes");
            }

            if (key == null || key.Length < 1)
            {
                throw new InvalidOperationException("Invalid encryption settings");
            }

            byte[] encrypted;

            try
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Mode = CipherMode.CBC;
                    aes.Key = key;
                    aes.KeySize = 256;
                    aes.BlockSize = 128;
                    Util.PrintByteArray(aes.IV);
                    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                    using (MemoryStream ms = new MemoryStream())
                    {
                        ms.Write(aes.IV, 0, aes.IV.Length);

                        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                        {
                            cs.Write(bytes, 0, bytes.Length);
                            cs.FlushFinalBlock();
                        }

                        encrypted = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
                throw new InvalidOperationException("Unable to encrypt data");
            }

            return encrypted;
        }

        public byte[] Decrypt(byte[] bytes)
        {
            if (bytes == null || bytes.Length < 1)
            {
                throw new ArgumentException("Invalid bytes");
            }

            if (key == null || key.Length < 1)
            {
                throw new InvalidOperationException("Invalid encryption settings");
            }

            byte[] decrypted;

            try
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Mode = CipherMode.CBC;
                    aes.Key = key;
                    aes.KeySize = 256;
                    aes.BlockSize = 128;
                    byte[] iv = new byte[16];
                    MemoryStream ms = new MemoryStream(bytes);
                    ms.Read(iv, 0, iv.Length);
                    Util.PrintByteArray(iv);
                    ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, iv);

                    using (ms)
                    {
                        using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                        {
                            using (BinaryReader br = new BinaryReader(cs))
                            {
                                decrypted = new byte[iv.Length];
                                br.Read(decrypted, 0, decrypted.Length - iv.Length);
                            }
                        }

                        decrypted = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
                throw new InvalidOperationException("Unable to decrypt data");
            }

            return decrypted;
        }
    }
}

Output:

Plain text: Hello World!
198 66 49 215 139 95 165 131 201 119 136 16 45 170 140 70 
Encrypted text: �B1׋_���w�-��F?�����8#*�����
239 191 189 66 49 215 139 95 239 191 189 239 191 189 239 191 
Decrypted text: �B1׋_���w�-��F?�����8#*�����

A solution to my issue and other advice will be greatly appreciated.

Upvotes: 1

Views: 2008

Answers (1)

benquinlan
benquinlan

Reputation: 301

I have included a working version of the code provided below:

using System;
using System.Text;
using System.IO;
using System.Linq;
using System.Security.Cryptography;

namespace EncryptionTest
{
    public class Aes256Handler
    {
        private byte[] key;

        public Aes256Handler(byte[] key)
        {
            this.key = key;
        }

        public string EncryptString(string plaintext)
        {
            return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(plaintext)));
        }

        public string DecryptString(string encryptedtext)
        {
            return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(encryptedtext)));
        }

        public byte[] Encrypt(byte[] bytes)
        {
            if (bytes == null || bytes.Length < 1)
            {
                throw new ArgumentException("Invalid bytes");
            }

            if (key == null || key.Length < 1)
            {
                throw new InvalidOperationException("Invalid encryption settings");
            }

            byte[] encrypted;

            try
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Key = key;
                    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                    using (MemoryStream ms = new MemoryStream())
                    {
                        ms.Write(aes.IV, 0, aes.IV.Length);

                        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                        {
                            cs.Write(bytes, 0, bytes.Length);
                        }

                        encrypted = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            return encrypted;
        }

        public byte[] Decrypt(byte[] bytes)
        {
            if (bytes == null || bytes.Length < 1)
            {
                throw new ArgumentException("Invalid bytes");
            }

            if (key == null || key.Length < 1)
            {
                throw new InvalidOperationException("Invalid encryption settings");
            }

            byte[] decrypted;

            try
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Key = key;
                    byte[] iv = new byte[16];
                    MemoryStream ms = new MemoryStream(bytes);
                    ms.Read(iv, 0, iv.Length);
                    ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, iv);

                    using (ms)
                    {
                        using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                        {
                            decrypted = new byte[bytes.Length - iv.Length];
                            var decryptedCount = cs.Read(decrypted, 0, decrypted.Length);
                            decrypted = decrypted.Take(decryptedCount).ToArray();

                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            return decrypted;
        }
    }
}

The main changes that were made were to use Convert.ToBase64String and Convert.FromBase64String when dealing with the encrypted byte array. I also removed the unnecessary declaration of parameters against the AES object as it was just setting the default values and I removed the unnecessary decrypted = ms.ToArray() from the decryption method.

Something else that it important is that when doing the decryption you want to get the number of bytes read. With the padding that is added due to block size for encryption without trimming your decrypted byte array you will find that you will end up with 0's at the array due to the padding.

Upvotes: 3

Related Questions