using Sarand.Net.Standard.Base.App; using System; using System.IO; using System.Security.Cryptography; namespace Sarand.Net.Standard.Base.Data { //======================================================================= public static class SacsAesIvCrypter { //------------------------------------------------------------------- // Properties. public static String AesIvDelimiter { get; } = ";"; public static String AesIvPrefix { get; } = "AESIV:"; //------------------------------------------------------------------- // Ref. https://xamarinhelp.com/cryptography-in-xamarin-forms/ // For reversible encryption, AES is generally the recommended standard. // AES 256 requires a 32 byte key to encrypt. // Either generate a 32 byte key or use any string and convert it into a key with CreateKey. // NB The salt will remain the same here for each key generated - it does not need to be randomized as with hashing. //------------------------------------------------------------------- private static byte[] CreateKey(string password, int keyBytes = 32) { byte[] salt = new byte[] { 80, 70, 60, 50, 40, 30, 20, 10 }; int iterations = 300; DeriveBytes keyGenerator = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256); return keyGenerator.GetBytes(keyBytes); } //------------------------------------------------------------------- // Encrypt String. // Return the encrypted text with the IV appended to the end. // In AES, the IV is a randomizer, making sure that no two identical messages ever give the same encrypted output. // The IV can be public and does not need to be hidden. //------------------------------------------------------------------- public static string Encrypt(this string clearValue, string encryptionKey) { using (Aes aes = Aes.Create()) { aes.Key = CreateKey(encryptionKey); byte[] aesIV = aes.IV; byte[] encrypted = Encrypt(clearValue, aes.Key, aesIV); return Convert.ToBase64String(encrypted) + AesIvDelimiter + Convert.ToBase64String(aesIV); } } //------------------------------------------------------------------- public static byte[] Encrypt(string plainText, byte[] key, byte[] iv) { if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException($"{nameof(plainText)}"); if (key == null || key.Length <= 0) throw new ArgumentNullException($"{nameof(key)}"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException($"{nameof(iv)}"); byte[] encrypted; using (Aes aes = Aes.Create()) { aes.Key = key; aes.IV = iv; using (MemoryStream memoryStream = new MemoryStream()) { using (ICryptoTransform encryptor = aes.CreateEncryptor()) using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) using (StreamWriter streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(plainText); } encrypted = memoryStream.ToArray(); } } return encrypted; } //------------------------------------------------------------------- // Decrypt String. // Decryption requires the encrypted value and the secret key. // This function is directly related to the above encryption process and will // assume that the IV is at the end of the string passed in. // To decrypt a message encrypted by another function, the IV will have to be passed explicitly. //------------------------------------------------------------------- public static string Decrypt(this string encryptedValue, string encryptionKey) { String[] valueParts = encryptedValue.Split(new String[] { AesIvDelimiter }, StringSplitOptions.None); if (valueParts.Length != 2) { SaLogger.Warn("SacsAesIvCrypter.Decrypt Missing IV - assuming not encrypted"); return encryptedValue; //throw new CryptographicException("Decrypt failure: Missing IV"); } return Decrypt(Convert.FromBase64String(valueParts[0]), CreateKey(encryptionKey), Convert.FromBase64String(valueParts[1])); } //------------------------------------------------------------------- public static string Decrypt(byte[] cipherText, byte[] key, byte[] iv) { if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException($"{nameof(cipherText)}"); if (key == null || key.Length <= 0) throw new ArgumentNullException($"{nameof(key)}"); if (iv == null || iv.Length <= 0) throw new ArgumentNullException($"{nameof(iv)}"); string plaintext = ""; using (Aes aes = Aes.Create()) { aes.Key = key; aes.IV = iv; using (MemoryStream memoryStream = new MemoryStream(cipherText)) using (ICryptoTransform decryptor = aes.CreateDecryptor()) using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) using (StreamReader streamReader = new StreamReader(cryptoStream)) plaintext = streamReader.ReadToEnd(); } return plaintext; } //------------------------------------------------------------------- } //======================================================================= }