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.