| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 | using System;using System.Diagnostics.CodeAnalysis;using System.IO;using System.Linq;using System.Security.Cryptography;using System.Text;namespace InABox.Core{    public static class Encryption    {        // This constant is used to determine the keysize of the encryption algorithm in bits.        // We divide this by 8 within the code below to get the equivalent number of bytes.        private const int Keysize = 256;        // This constant determines the number of iterations for the password bytes generation function.        private const int DerivationIterations = 1000;        public static string Encrypt(string plainText, string passPhrase, bool simple = false)        {            if (simple)            {                var encrypted = new byte[plainText.Length];                for (var i = 0; i < plainText.Length; i++)                {                    var ch = plainText[i];                    encrypted[i] = (byte)(ch + passPhrase[i % passPhrase.Length]);                }                return Convert.ToBase64String(encrypted);            }            // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text            // so that the same Salt and IV values can be used when decrypting.              var saltStringBytes = Generate256BitsOfRandomEntropy();            var ivStringBytes = Generate256BitsOfRandomEntropy();            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))            {                var keyBytes = password.GetBytes(Keysize / 8);                using (var symmetricKey = new RijndaelManaged())                {                    symmetricKey.BlockSize = 256;                    symmetricKey.Mode = CipherMode.CBC;                    symmetricKey.Padding = PaddingMode.PKCS7;                    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))                    {                        using (var memoryStream = new MemoryStream())                        {                            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))                            {                                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);                                cryptoStream.FlushFinalBlock();                                // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.                                var cipherTextBytes = saltStringBytes;                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();                                memoryStream.Close();                                cryptoStream.Close();                                return Convert.ToBase64String(cipherTextBytes);                            }                        }                    }                }            }        }        public static string Decrypt(string cipherText, string passPhrase, bool simpleEncryption = true)        {            if (string.IsNullOrWhiteSpace(cipherText))                throw new Exception("Text may not be blank!");            // New Simplified Decryption to handle .Net6 clients            // Kenric needs this to get the GenHTTP server running            // Needs to be revisited (stronger encryption?  TLS/SSL?) when moving to .Net6 overall            if (cipherText.Length < 64 || simpleEncryption == true)            {                var encrypted = Convert.FromBase64String(cipherText);                var decrypted = new byte[encrypted.Length];                for (var i = 0; i < encrypted.Length; i++)                {                    var ch = encrypted[i];                    decrypted[i] = (byte)(ch - passPhrase[i % passPhrase.Length]);                }                return Encoding.UTF8.GetString(decrypted);            }            // Get the complete stream of bytes that represent:            // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);            // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();            // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();            // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8 * 2).Take(cipherTextBytesWithSaltAndIv.Length - Keysize / 8 * 2)                .ToArray();            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))            {                var keyBytes = password.GetBytes(Keysize / 8);                using (var symmetricKey = new RijndaelManaged())                {                    symmetricKey.BlockSize = 256;                    symmetricKey.Mode = CipherMode.CBC;                    symmetricKey.Padding = PaddingMode.PKCS7;                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))                    {                        using (var memoryStream = new MemoryStream(cipherTextBytes))                        {                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))                            {                                var plainTextBytes = new byte[cipherTextBytes.Length];                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);                                memoryStream.Close();                                cryptoStream.Close();                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);                            }                        }                    }                }            }        }        public static bool Decrypt(string cipherText, string passPhrase, out string decrypted)        {            decrypted = "";            var result = false;            try            {                decrypted = Decrypt(cipherText, passPhrase);                result = true;            }            catch (Exception e)            {            }            return result;        }        public static byte[] Generate256BitsOfRandomEntropy()        {            var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.            using (var rngCsp = new RNGCryptoServiceProvider())            {                // Fill the array with cryptographically secure random bytes.                rngCsp.GetBytes(randomBytes);            }            return randomBytes;        }        public static string EncryptV2(string data, byte[] key)        {            if (TryEncryptV2(data, key, out var result, out var error))            {                return result;            }            throw new Exception(error);        }        public static bool TryEncryptV2(string data, byte[] key, [NotNullWhen(true)] out string? result, [NotNullWhen(false)] out string? error)        {            try            {                using var stream = new MemoryStream();                using (var aes = Aes.Create())                {                    aes.Key = key;                    var iv = aes.IV;                    stream.Write(iv, 0, iv.Length);                    using var cryptoStream = new CryptoStream(stream, aes.CreateEncryptor(), CryptoStreamMode.Write);                    using var writer = new StreamWriter(cryptoStream);                    writer.Write(data);                }                result = Convert.ToBase64String(stream.ToArray());                error = null;                return true;            }            catch(Exception e)            {                error = $"Encryption failed! {CoreUtils.FormatException(e)}";                result = null;                return false;            }        }        public static string DecryptV2(string data, byte[] key)        {            if(TryDecryptV2(data, key, out var result, out var error))            {                return result;            }            throw new Exception(error);        }        public static bool TryDecryptV2(string data, byte[] key, [NotNullWhen(true)] out string? result, [NotNullWhen(false)] out string? error)        {            try            {                using var stream = new MemoryStream(Convert.FromBase64String(data));                using var aes = Aes.Create();                var iv = new byte[aes.IV.Length];                if (stream.Read(iv, 0, iv.Length) != iv.Length)                {                    error = $"Decryption failed! Data is too short for IV.";                    result = null;                    return false;                }                using var cryptoStream = new CryptoStream(stream, aes.CreateDecryptor(key, iv), CryptoStreamMode.Read);                using var decryptReader = new StreamReader(cryptoStream);                var decrypted = decryptReader.ReadToEnd();                result = decrypted;                error = null;                return true;            }            catch(Exception e)            {                error = $"Decryption failed! {CoreUtils.FormatException(e)}";                result = null;                return false;            }        }    }}
 |