C# AES 256 bits Encryption Library with Salt

What is encryption? Here’s a simple explanation. Take the following sentence as example:

I have a pen

When this sentence combined with a password (key),

(I have a pen) + (password/key)

it can be encrypted (or scrambled+scartered) and become the following meaningless random looked text:

nBo/OJS7jmXNdKbC/143cA==

Above text can be carried around openly through middle persons and finally reached the target receiver. Then, receiver will use the same password to reverse it (or decrypt it), revealing the original text:

(nBo/OJS7jmXNdKbC/143cA==) + (password/key)  <<== reverse the process
=> I have a pen

Simple History of AES Encryption

In year 2001, U.S. National Institute of Standards and Technology (NIST), holds a competition to call experts from nation wide to invent a better encryption algorithm. After evaluation, the algorithm of “Rijndael” which was designed by Joan Daemen and Vincent Rijmen had been chosen as the winner. Thus, Rijndael has become the new age global standard of encryption algorithm. This algorithm has been adopted by the U.S. government.

C# Implementation of AES Encryption

Begin by adding the following using statements to import the encryption library:

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

Encryption

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, 1000);
            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;
}

Decryption

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, 1000);
            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;
}

Example of Encrypting String

You’ll notice that the encrypted string is stored in base64 encoded mode. For those who is not familiar with base64 encoding, you may want to read:

Encrypt String

public string EncryptText(string input, string password)
{
    // Get the bytes of the string
    byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

    // Hash the password with SHA256
    passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);

    string result = Convert.ToBase64String(bytesEncrypted);

    return result;
}

Decrypt String

public string DecryptText(string input, string password)
{
    // Get the bytes of the string
    byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
    passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);

    string result = Encoding.UTF8.GetString(bytesDecrypted);

    return result;
}

Getting Randomized Encryption Result with Salt

If we encrypt the same context (i.e. string of “Hello World”) for 10 times, the encrypted results will be the same. What if we want the results different from each time it is encrypted?

What you can do is by appending a random salt bytes in front of the original bytes before encryption, and remove it after decryption.

Example of Appending Randomized Salt Before Encrypting a String

public static string EncryptString(string text, string password)
{
    byte[] baPwd = Encoding.UTF8.GetBytes(password);

    // Hash the password with SHA256
    byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);

    byte[] baText = Encoding.UTF8.GetBytes(text);

    byte[] baSalt = GetRandomBytes();
    byte[] baEncrypted = new byte[baSalt.Length + baText.Length];

    // Combine Salt + Text
    for (int i = 0; i < baSalt.Length; i++)
        baEncrypted[i] = baSalt[i];
    for (int i = 0; i < baText.Length; i++)
        baEncrypted[i + baSalt.Length] = baText[i];

    baEncrypted = AES_Encrypt(baEncrypted, baPwdHash);

    string result = Convert.ToBase64String(baEncrypted);
    return result;
}

Example of Removing the Salt after Decryption

public static string DecryptString(string text, string password)
{
    byte[] baPwd = Encoding.UTF8.GetBytes(password);

    <em>//</em><em> Hash the password with SHA256</em>
    byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);

    byte[] baText = Convert.FromBase64String(text);

    byte[] baDecrypted = AES_Decrypt(baText, baPwdHash);

    <em>//</em><em> Remove salt</em>
    int saltLength = GetSaltLength();
    byte[] baResult = new byte[baDecrypted.Length - saltLength];
    for (int i = 0; i < baResult.Length; i++)
        baResult[i] = baDecrypted[i + saltLength];

    string result = Encoding.UTF8.GetString(baResult);
    return result;
}

Code for getting random bytes

public static byte[] GetRandomBytes()
{
    int saltLength = GetSaltLength();
    byte[] ba = new byte[saltLength];
    RNGCryptoServiceProvider.Create().GetBytes(ba);
    return ba;
}

public static int GetSaltLength()
{
    return 8;
}

Another way of getting random bytes is by using System.Random. However, System.Random is strongly not recommended to be used in cryptography. This is because System.Random is not a true random. The changes of the value are following a specific sequence and pattern and it is predictable. RNGCrypto is truly randomize and the generated values does not follow a pattern and it is unpredictable.