using System; using System.Security.Cryptography; using System.Text; class AesDemo { const int HASH_SIZE = 32; //SHA256 /// Performs encryption with random IV (prepended to output), and includes hash of plaintext for verification. public static byte[] Encrypt(string password, byte[] passwordSalt, byte[] plainText) { // Construct message with hash var msg = new byte[HASH_SIZE + plainText.Length]; var hash = computeHash(plainText, 0, plainText.Length); Buffer.BlockCopy(hash, 0, msg, 0, HASH_SIZE); Buffer.BlockCopy(plainText, 0, msg, HASH_SIZE, plainText.Length); // Encrypt using (var aes = createAes(password, passwordSalt)) { aes.GenerateIV(); using (var enc = aes.CreateEncryptor()) { var encBytes = enc.TransformFinalBlock(msg, 0, msg.Length); // Prepend IV to result var res = new byte[aes.IV.Length + encBytes.Length]; Buffer.BlockCopy(aes.IV, 0, res, 0, aes.IV.Length); Buffer.BlockCopy(encBytes, 0, res, aes.IV.Length, encBytes.Length); return res; } } } public static byte[] Decrypt(string password, byte[] passwordSalt, byte[] cipherText) { using (var aes = createAes(password, passwordSalt)) { var iv = new byte[aes.IV.Length]; Buffer.BlockCopy(cipherText, 0, iv, 0, iv.Length); aes.IV = iv; // Probably could copy right to the byte array, but that's not guaranteed using (var dec = aes.CreateDecryptor()) { var decBytes = dec.TransformFinalBlock(cipherText, iv.Length, cipherText.Length - iv.Length); // Verify hash var hash = computeHash(decBytes, HASH_SIZE, decBytes.Length - HASH_SIZE); var existingHash = new byte[HASH_SIZE]; Buffer.BlockCopy(decBytes, 0, existingHash, 0, HASH_SIZE); if (!compareBytes(existingHash, hash)){ throw new CryptographicException("Message hash incorrect."); } // Hash is valid, we're done var res = new byte[decBytes.Length - HASH_SIZE]; Buffer.BlockCopy(decBytes, HASH_SIZE, res, 0, res.Length); return res; } } } static bool compareBytes(byte[] a1, byte[] a2) { if (a1.Length != a2.Length) return false; for (int i = 0; i < a1.Length; i++) { if (a1[i] != a2[i]) return false; } return true; } static Aes createAes(string password, byte[] salt) { // Salt may not be needed if password is safe if (password.Length < 8) throw new ArgumentException("Password must be at least 8 characters.", "password"); if (salt.Length < 8) throw new ArgumentException("Salt must be at least 8 bytes.", "salt"); var pdb = new PasswordDeriveBytes(password, salt, "SHA512", 129); var key = pdb.GetBytes(16); var aes = Aes.Create(); aes.Mode = CipherMode.CBC; aes.Key = pdb.GetBytes(aes.KeySize / 8); return aes; } static byte[] computeHash(byte[] data, int offset, int count) { using (var sha = SHA256.Create()) { return sha.ComputeHash(data, offset, count); } } public static void Main() { var password = "1234567890!"; var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; var ct1 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct1")); Console.WriteLine(Convert.ToBase64String(ct1)); var ct2 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct2")); Console.WriteLine(Convert.ToBase64String(ct2)); var pt1 = Decrypt(password, salt, ct1); Console.WriteLine(Encoding.UTF8.GetString(pt1)); var pt2 = Decrypt(password, salt, ct2); Console.WriteLine(Encoding.UTF8.GetString(pt2)); // Now check tampering try { ct1[30]++; Decrypt(password, salt, ct1); Console.WriteLine("Error: tamper detection failed."); } catch (Exception ex) { Console.WriteLine("Success: tampering detected."); Console.WriteLine(ex.ToString()); } } }