I've been researching AES encryption a bit over the past several days. The official (MSDN) examples I've seen are encrypting and decrypting using the same AES instance. They don't go in to what to do when generating and saving an encrypted value with AES and needing to decrypt it later with another AES instance.
I came up with the following and am wondering if there is anything wrong with it, aside from defaulting the password to a static value (I will be developing something else to manage encryption passwords)? It generates a random salt on encryption and stores it with the encrypted cipher prior to Base64 encoding. This assures that running the encryption twice on the same input does not result in the same cipher text.
public static string Encrypt(string plainText, string password = "BadgersAreAwesome")
{
if (plainText == null)
throw new ArgumentNullException("plainText");
if (password == null)
throw new ArgumentNullException("password");
// Will return the cipher text
string cipherText = "";
// Utilizes helper function to generate random 16 byte salt using RNG
byte[] salt = GenerateSaltBytes(SaltSize);
// Convert plain text to bytes
byte[] plainBytes = Encoding.Unicode.GetBytes(plainText);
// create new password derived bytes using password/salt
using (Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt))
{
using (Aes aes = AesManaged.Create())
{
// Generate key and iv from password/salt and pass to aes
aes.Key = pdb.GetBytes(aes.KeySize / 8);
aes.IV = pdb.GetBytes(aes.BlockSize / 8);
// Open a new memory stream to write the encrypted data to
using (MemoryStream ms = new MemoryStream())
{
// Create a crypto stream to perform encryption
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
// write encrypted bytes to memory
cs.Write(plainBytes, 0, plainBytes.Length);
}
// get the cipher bytes from memory
byte[] cipherBytes = ms.ToArray();
// create a new byte array to hold salt + cipher
byte[] saltedCipherBytes = new byte[salt.Length + cipherBytes.Length];
// copy salt + cipher to new array
Array.Copy(salt, 0, saltedCipherBytes, 0, salt.Length);
Array.Copy(cipherBytes, 0, saltedCipherBytes, salt.Length, cipherBytes.Length);
// convert cipher array to base 64 string
cipherText = Convert.ToBase64String(saltedCipherBytes);
}
aes.Clear();
}
}
return cipherText;
}
public static string Decrypt(string cipherText, string password = "BadgersAreAwesome")
{
if (cipherText == null)
throw new ArgumentNullException("cipherText");
if (password == null)
throw new ArgumentNullException("password");
// will return plain text
string plainText = "";
// get salted cipher array
byte[] saltedCipherBytes = Convert.FromBase64String(cipherText);
// create array to hold salt
byte[] salt = new byte[SaltSize];
// create array to hold cipher
byte[] cipherBytes = new byte[saltedCipherBytes.Length - salt.Length];
// copy salt/cipher to arrays
Array.Copy(saltedCipherBytes, 0, salt, 0, salt.Length);
Array.Copy(saltedCipherBytes, salt.Length, cipherBytes, 0, saltedCipherBytes.Length-salt.Length);
// create new password derived bytes using password/salt
using (Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt))
{
using (Aes aes = AesManaged.Create())
{
// Generate key and iv from password/salt and pass to aes
aes.Key = pdb.GetBytes(aes.KeySize / 8);
aes.IV = pdb.GetBytes(aes.BlockSize / 8);
// Open a new memory stream to write the encrypted data to
using (MemoryStream ms = new MemoryStream())
{
// Create a crypto stream to perform decryption
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
// write decrypted data to memory
cs.Write(cipherBytes, 0, cipherBytes.Length);
}
// convert decrypted array to plain text string
plainText = Encoding.Unicode.GetString(ms.ToArray());
}
aes.Clear();
}
}
return plainText;
}