Sunday, November 25, 2012

Transferring Data Securely - Part 2 - Symmetric Cryptography

This is the second post of a multi-part post in which I will cover how to sign a message, encrypt it using Symmetric cryptography (with a random key), and then Asymmetrically encrypt the random key.  In my previous post, I covered how to obtain a digital signature for a set of data.  In this post, I will show you how to symmetrically encrypt the data using a randomly generated key and initialization vector (IV).

To make things easier, I like to use a Symmetric utility with state, but this is just personal preference.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace BlogSandbox.encryption
{
    public class SymmetricEncryption
    {
        public SymmetricAlgorithm Provider { get; set; }

        public SymmetricEncryption
            (SymmetricAlgorithm provider, 
            int keySize)
        {
            Provider = provider;
            provider.KeySize = keySize;
        }

        public SymmetricEncryption(
            SymmetricAlgorithm provider, 
            byte[] key, 
            byte[] iv)
        {
            Provider = provider;
            provider.Key = key;
            provider.IV = iv;
        }

        public byte[] GetKey() 
        { 
            return Provider.Key;    
        }

        public byte[] GetIv() 
        {
            return Provider.IV;
        }

        public byte[] Encrypt(byte[] message)
        {
            return TransformMessage(message, 
                Provider.CreateEncryptor());
        }

        public string Encrypt(string message)
        {
            byte[] encrypted = TransformMessage(
                        Encoding.UTF8.GetBytes(message), 
                        Provider.CreateEncryptor());
            return Convert.ToBase64String(encrypted);
        }

        public byte[] Decrypt(byte[] message)
        {
            return TransformMessage(message,
                Provider.CreateDecryptor());
        }

        public string Decrypt(string message)
        {
            byte[] decrypted = TransformMessage(
                        Convert.FromBase64String(message),
                        Provider.CreateDecryptor());
            return Encoding.UTF8.GetString(decrypted);
        }

        private byte[] TransformMessage(byte[] message, 
                        ICryptoTransform cryptoTransform)
        {
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(
                                    memoryStream, 
                                    cryptoTransform, 
                                    CryptoStreamMode.Write);

            // write message to be encrypted and flush
            cryptoStream.Write(message, 0, message.Length);
            cryptoStream.FlushFinalBlock();

            // read encrypted message into a byte[]
            memoryStream.Position = 0;
            byte[] result = memoryStream.ToArray();

            // close up the streams
            memoryStream.Close();
            cryptoStream.Close();

            return result;
        }
    }
}
The majority of work being done here is in the TransformMessage method.  By taking in the ICryptoTransform, we can handle both the encryption and the decryption using the same method.  The CryptoStream does all the work for us by converting the message using the encryptor or decryptor and placing the data in the MemoryStream.  I have included the "String" methods here because it is a bit tricky to get the encoding/decoding right.  Notice that when you get a string to be encrypted, you must convert it using the Encoding.UTF8.GetBytes method, but when returning the encrypted string you return it using the Convert.ToBase64String method.  This is very important, if you convert it in UTF8, you will lose data and not be able to decrypt it again.  When performing the decrypt, you do the opposite, get the bytes using the Convert.FromBase64String and convert the bytes using the Encoding.UTF8.GetString method.

When using this utility, you have the option of passing in your own key/IV or having it randomly generated by the provider.  In the test code, I will simply have it generated by the provider and is the use case for all of these posts. Here is some test code:

namespace BlogSandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            string testText = "encrypt and decrypt me";

            SymmetricEncryption enc = new SymmetricEncryption(
                CryptoConfig.CreateFromName("AesManaged")
                    as SymmetricAlgorithm,
                256);

            string encText = enc.Encrypt(testText);
            Console.WriteLine("Encrypted: " + encText);
            string decText = enc.Decrypt(encText);
            Console.WriteLine("Decrypted: " + decText);
            Thread.Sleep(20000);
        }
    }
}
Notice how I obtained the provider "CryptoConfig.CreateFromName" - this takes advantage of Cryptographic Agility.  With cryptographic agility, you can assign different algorithms to a name (like "SymmAlgorithm") so that you can switch them for all programs using your library (configured per server).  While a very important topic, I won't be discussing this here... however, I strongly encourage you to google it and learn about it.

That's it for symmetric encryption/decryption.  In the next post I will cover Asymmetric encryption/decryption...

No comments:

Post a Comment

Followers