Thursday, 22 June 2017

Symmetric Encryption

SymmetricEncryption is also known as shared secret encryption it requires a secret key and Initialization Vector for decryption. the main drawback of Symmetric Encryption is that it's only as secure as the key, should the key be compromised then the encrypted data is no longer secure.

Symmetric algorithms in .net work utilizing "cipher block chaining"; this works by taking data that's bigger then a predefined block size and braking it up into blocks of equal size; if the last block is too small it's padded with data.

The first block is encrypted using the Initialization Vector and the key, the following block is then encrypted using the result from the first block instead of the IV but using the same key, and so on until all the blocks are encrypted.

The reason this is done is to ensure that different data that has the same blocks does not encrypt to the same result, and thus can't be reversed engineered.

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

namespace pc.symmetricEncryptionExample
{
    class Program
    {
        static byte[] Encrypt(string data, out byte[] Key, out byte[] Iv)
        {
            var cryptoAlgorythm = SymmetricAlgorithm.Create("Rijndael");

            //create and set IV & Key
            cryptoAlgorythm.GenerateIV();
            Iv = cryptoAlgorythm.IV;

            cryptoAlgorythm.GenerateKey();
            Key = cryptoAlgorythm.Key;

            ICryptoTransform encryptor =
                cryptoAlgorythm.CreateEncryptor(Key, Iv);

            var plainData = Encoding.ASCII.GetBytes(data);
            return encryptor.TransformFinalBlock(plainData, 0, plainData.Length);
        }

        static byte[] Decrypt(byte[] Data, byte[] Key, byte[] Iv) {
            var cryptoAlgorythm = SymmetricAlgorithm.Create("Rijndael");
           
            ICryptoTransform decryptor =
                cryptoAlgorythm.CreateDecryptor(Key, Iv);

            return decryptor.TransformFinalBlock(Data, 0, Data.Length);
        }

        static void Main(string[] args)
        {
            var data = "Hello world, this is my secret data i want encrypted";
            byte[] iv;
            byte[] key;

            //encrypted data
            byte[] cipherData = Encrypt(data, out key, out iv);

            //display encrypted data
            Console.WriteLine("Encrypted data");
            Console.WriteLine(Encoding.ASCII.GetString(cipherData));

            //display key
            Console.WriteLine("Key");
            Console.WriteLine(Encoding.ASCII.GetString(key));
           
            //display Decrypted Data
            var decryptedData = Decrypt(cipherData, key, iv);
            Console.WriteLine("Decrypted data");
            Console.WriteLine(Encoding.ASCII.GetString(decryptedData));
        }
    }
}


now in the above we create our key from our Symmetric Encryption class, generally one of the more valuable  characteristics of a password is to remember it, so generating a random byte array may not be the best suited in fulfilling this criteria.

So let's do that, first let's find out what the criteria for our password must be, luckily the SymmetricAlgorithm  Class has a function for just that

var cryptoAlgorythm = SymmetricAlgorithm.Create("Rijndael");

foreach (var ks in cryptoAlgorythm.LegalKeySizes)
    Console.WriteLine($"{ks.MinSize} {ks.MaxSize} {ks.SkipSize}");

The result of this is 128, 256 and 64, now in bytes that is 16, 32 and 8, so what does that mean? well this tells us that our password must be at least 16 bytes long no longer than 32 bytes and must increase in increments of 8 bytes or in bits 128, 192, 256.

a bit convoluted? sure is, doesn't help that there's a bug that'll let you pass invalid key lengths (< 128) lengths, but there's a ValidKeyLength(size in bits) function.

if (cryptoAlgorythm.ValidKeySize(Key.Length * 8))
    Console.WriteLine("Valid key size");

just make sure to multiply your byte array's length by 8, since there's 8 bits in a byte.

Now let's create a function to take in a password with size in bits parameter and return a byte array we can use as our key in our encryption function.

static byte[] GetPassword(string Password, int RequiredSizeInBits)
{
    var password = Encoding.Default.GetBytes(Password);
           
    if (RequiredSizeInBits > (password.Length * 8))
        Array.Resize(ref password, (RequiredSizeInBits / 8));
           
    return password;

}

We skimped on the error catching, but the meat is there, now we can redo our example with a user defined password

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

namespace pc.symmetricEncryptionExample
{
    class Program
    {
        static byte[] Encrypt(string data, byte[] Key, out byte[] Iv)
        {
            var cryptoAlgorythm = SymmetricAlgorithm.Create("Rijndael");

            //create and set IV & Key
            cryptoAlgorythm.GenerateIV();
            Iv = cryptoAlgorythm.IV;
           
            if (cryptoAlgorythm.ValidKeySize(Key.Length * 8))
                Console.WriteLine("Valid");

            ICryptoTransform encryptor =
                cryptoAlgorythm.CreateEncryptor(Key, Iv);

            var plainData = Encoding.ASCII.GetBytes(data);
            return encryptor.TransformFinalBlock(plainData, 0, plainData.Length);
        }

        static byte[] Decrypt(byte[] Data, byte[] Key, byte[] Iv)
        {
            var cryptoAlgorythm = SymmetricAlgorithm.Create("Rijndael");

            ICryptoTransform decryptor = cryptoAlgorythm.CreateDecryptor(Key, Iv);
            return decryptor.TransformFinalBlock(Data, 0, Data.Length);
        }

        static byte[] GetPassword(string Password, int RequiredSizeInBits)
        {
            var password = Encoding.Default.GetBytes(Password);

            if (RequiredSizeInBits > (password.Length * 8))
                Array.Resize(ref password, (RequiredSizeInBits / 8));
            return password;
        }
        static void Main(string[] args)
        {
            var data = "Hello world, this is my secret data i want encrypted";
            var password = "pass";

            var Iv = new byte[16];
            var Key = GetPassword(password, 128);
           
            //encrypted data
            byte[] cipherData = Encrypt(data, Key, out Iv);

            //display encrypted data
            Console.WriteLine("Encrypted data");
            Console.WriteLine(Encoding.ASCII.GetString(cipherData));

            //display Decrypted Data
            var decryptedData = Decrypt(cipherData, Key, Iv);
            Console.WriteLine("Decrypted data");
            Console.WriteLine(Encoding.ASCII.GetString(decryptedData));
        }
    }
}

Now this is a pretty contrived example, obviously to be more secure you wouldn't want just a four letter pass in a big byte array.

In our examples we've been using the static SymmetricAlgorithm.Create("Rijndael"); function, however there are many different options for Symmetric Encryption:


now despite all of these options which all derive from the SymmetricAlgorithm class, AesManaged is the gold standard, it's the ISO standard and the ubiquitous preference around the world.

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

namespace pc.symmetricEncryptionExample03
{
    class Program
    {
        static byte[] Encrypt(string data, byte[] Key, out byte[] Iv)
        {
            using (var aesAlgorythm = new AesManaged())
                try {
                    aesAlgorythm.GenerateIV();
                    Iv = aesAlgorythm.IV;
                    aesAlgorythm.Key = Key;

                    var encryptor = aesAlgorythm.CreateEncryptor();

                    using (var ms = new MemoryStream()) {
                    using (var cs=new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                    using (var sw = new StreamWriter(cs))
                        sw.Write(data);
                    return ms.ToArray(); }
                }
                finally {
                    aesAlgorythm.Clear();
                }
        }

        static string Decrypt(byte[] EncryptedData, byte[] Key, byte[] Iv)
        {
            using (var aesAlgorythm = new AesManaged())
                try {
                    var decryptor = aesAlgorythm.CreateDecryptor(Key, Iv);

                    using (var ms = new MemoryStream(EncryptedData))
                    using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                    using (var sr = new StreamReader(cs))
                        return sr.ReadToEnd();
                }
                finally {
                    aesAlgorythm.Clear();
                }
        }

        static byte[] GetPassword(string Password, int RequiredSizeInBits)
        {
            var password = Encoding.Default.GetBytes(Password);

            if (RequiredSizeInBits > (password.Length * 8))
                Array.Resize(ref password, (RequiredSizeInBits / 8));
            return password;
        }

        static void Main(string[] args)
        {
            var data = "Hello world, this is my secret data i want encrypted";
            var password = "pass";

            var Iv = new byte[16];
            var Key = GetPassword(password, 128);

            //encrypted data
            byte[] cipherData = Encrypt(data, Key, out Iv);

            //display encrypted data
            Console.WriteLine("Encrypted data");
            Console.WriteLine(Encoding.Default.GetString(cipherData));

            //display Decrypted Data
            Console.WriteLine("Decrypted data");
            Console.WriteLine(Decrypt(cipherData, Key, Iv));
        }
    }
}

above we leveraged the AesManaged class for our encryption, one thing to notics is that we call the clear method in a finally clause, this 0's out any potentially sensitive data like our key for example.