I need to store sensitive strings in DB, so decided to encrypt it in a DB and decrypt it at the application layer. And noticed that it is not so easy to find ready to use an example. Please check my code below. It is based on this damienbod's topic, but I included IV to the string itself and also want to know if it is production-ready. And a somewhat unrelated question: probably I should use an old Rijndael solution like this?
public class SymmetricEncryptDecrypt
{
private const int ivBytes = 128;
public (string Key, string IVBase64) InitSymmetricEncryptionKeyIV()
{
var key = GetEncodedRandomString(32); // 256
using (Aes cipher = CreateCipher(key))
{
cipher.GenerateIV();
var IVBase64 = Convert.ToBase64String(cipher.IV);
return (key, IVBase64);
}
}
private byte[] GetNewIv()
{
using (Aes cipher = CreateCipher(GetEncodedRandomString(32)))
{
cipher.GenerateIV();
return cipher.IV;
}
}
/// <summary>
/// Encrypt using AES
/// </summary>
/// <param name="text">any text</param>
/// <param name="IV">Base64 IV string/param>
/// <param name="key">Base64 key</param>
/// <returns>Returns an encrypted string</returns>
public string Encrypt(string text, string key)
{
var iv = this.GetNewIv();
using (Aes cipher = CreateCipher(key))
{
cipher.IV = iv;
ICryptoTransform cryptTransform = cipher.CreateEncryptor();
byte[] plaintext = Encoding.UTF8.GetBytes(text);
byte[] cipherText = cryptTransform.TransformFinalBlock(plaintext, 0, plaintext.Length);
return Convert.ToBase64String(iv.Concat(cipherText).ToArray());
}
}
/// <summary>
/// Decrypt using AES
/// </summary>
/// <param name="text">Base64 string for an AES encryption</param>
/// <param name="key">Base64 key</param>
/// <returns>Returns a string</returns>
public string Decrypt(string encryptedText, string key)
{
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(encryptedText);
var ivStringBytes = cipherTextBytesWithSaltAndIv.Take(ivBytes / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(ivBytes / 8).Take(cipherTextBytesWithSaltAndIv.Length - (ivBytes / 8)).ToArray();
using (Aes cipher = CreateCipher(key))
{
cipher.IV = ivStringBytes;
ICryptoTransform cryptTransform = cipher.CreateDecryptor();
byte[] plainBytes = cryptTransform.TransformFinalBlock(cipherTextBytes, 0, cipherTextBytes.Length);
return Encoding.UTF8.GetString(plainBytes);
}
}
private string GetEncodedRandomString(int length)
{
var base64 = Convert.ToBase64String(GenerateRandomBytes(length));
return base64;
}
/// <summary>
/// Create an AES Cipher using a base64 key
/// </summary>
/// <param name="key"></param>
/// <returns>AES</returns>
private Aes CreateCipher(string keyBase64)
{
// Default values: Keysize 256, Padding PKC27
Aes cipher = Aes.Create();
cipher.Mode = CipherMode.CBC; // Ensure the integrity of the ciphertext if using CBC
cipher.Padding = PaddingMode.ISO10126;
cipher.Key = Convert.FromBase64String(keyBase64);
return cipher;
}
private byte[] GenerateRandomBytes(int length)
{
var byteArray = new byte[length];
RandomNumberGenerator.Fill(byteArray);
return byteArray;
}
}