Sunday, October 7, 2012

C# AES Encryption & Decryption Methods

I had a hard time finding a set of functions to handle AES encryption and decryption correctly so I figured I would share working ones with the world.

Suppose the application has it's secret values securely loaded. Below is not a secure way to handle this, but for the purpose of this post we will be overly transparent.
key = "PasswordP@s$w0rdPa$$w0rdP@ssw0rd";
salt = "S@lt$@ltSa|tSaltS@lt$a|t$@|tSalT";
iv = "iv1^!vi^1viV!^iv"; // length must be 16 bytes
Here is an example of it's potential use.
cipherText = clearText.Encrypt(key, salt, iv);
clearText2 = cipherText.Decrypt(key, salt, iv);
Below is the section of code that handles the encryption and decryption of the strings. Feel free to change 72 to a different number; if you change it here, you MUST change the Decrypt function to use the same number of rounds. This is used to figure out how many rounds of encryption will occur. Note that fewer rounds is faster but potentially less secure and the opposite for more rounds.
/// 
/// Encrypts text given default values
/// 
/// /// /// Password Salt value/// 16 character string/// 
public static string Encrypt(this string PlainText, string Password, string Salt, string InitialVector)
{
    return PlainText.Encrypt(Password, Salt, "SHA1", 72, InitialVector, 256);
}

/// 
/// Encrypts a string given set of parameters
/// 
/// /// /// /// /// /// /// /// 
public static string Encrypt(this string PlainText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    try
    {
        byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
        byte[] CipherTextBytes;
        using (ICryptoTransform Encryptor = CreateRijndael(Password, Salt, HashAlgorithm, PasswordIterations, InitialVector, KeySize, CryptoTransformDirection.Encrypt))
        {
            using (MemoryStream MemStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                    cryptoStream.FlushFinalBlock();
                    CipherTextBytes = MemStream.ToArray();
                }
            }
        }
        return Convert.ToBase64String(CipherTextBytes);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

/// 
/// Decrypts text given default values
/// 
/// /// /// Password Salt value/// 16 character string/// 
public static string Decrypt(this string CipherText, string Password, string Salt, string InitialVector)
{
    return CipherText.Decrypt(Password, Salt, "SHA1", 72, InitialVector, 256);
}

/// 
/// Decrypts a string given set of parameters
/// 
/// /// /// /// /// /// /// /// 
public static string Decrypt(this string CipherText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    try
    {
        byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
        byte[] PlainTextBytes;
        int ByteCount;
        using (ICryptoTransform Decryptor = CreateRijndael(Password, Salt, HashAlgorithm, PasswordIterations, InitialVector, KeySize, CryptoTransformDirection.Decrypt))
        {
            using (MemoryStream MemStream = new MemoryStream(CipherTextBytes))
            {
                using (CryptoStream cryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read))
                {
                    PlainTextBytes = new byte[CipherTextBytes.Length];
                    ByteCount = cryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
                }
            }
        }
        return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
    }
    catch (Exception ex)
    {
        //return String.Empty;
        throw ex;
    }
}

/// 
/// Enumeration to define which direction the crypto is going
/// 
public enum CryptoTransformDirection {
    Encrypt,
    Decrypt
}

/// 
/// Gets a transform object which will perform the Encryption/Decryption
/// 
/// /// /// /// /// /// /// Encrypt/Decrypt/// 
public static ICryptoTransform CreateRijndael(string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize, CryptoTransformDirection direction)
{
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;
    if (direction == CryptoTransformDirection.Decrypt)
    {
        return SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes);
    }
    else
    {
        return SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
    }

}

No comments: