radidim

symetricEncryptor.cs

Nov 5th, 2021 (edited)
1,327
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.49 KB | None | 0 0
  1. // From https://tomrucki.com/posts/aes-encryption-in-csharp/
  2. //Disclaimer: The creator of 'C# Shell (C# Offline Compiler)' is in no way responsible for the code posted by any user.
  3. using System;
  4. using System.Linq;
  5. using System.Security.Cryptography;
  6. using System.Text;
  7.  
  8. public static class SymmetricEncryptor
  9. {
  10.     private const int AesBlockByteSize = 128 / 8;
  11.  
  12.     private const int PasswordSaltByteSize = 128 / 8;
  13.     private const int PasswordByteSize = 256 / 8;
  14.     private const int PasswordIterationCount = 100_000;
  15.  
  16.     private const int SignatureByteSize = 256 / 8;
  17.  
  18.     private const int MinimumEncryptedMessageByteSize =
  19.         PasswordSaltByteSize + // auth salt
  20.         PasswordSaltByteSize + // key salt
  21.         AesBlockByteSize + // IV
  22.         AesBlockByteSize + // cipher text min length
  23.         SignatureByteSize; // signature tag
  24.  
  25.     private static readonly Encoding StringEncoding = Encoding.UTF8;
  26.     private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
  27.  
  28.     public static byte[] EncryptString(string toEncrypt, string password)
  29.     {
  30.         // encrypt
  31.         var keySalt = GenerateRandomBytes(PasswordSaltByteSize);
  32.         var key = GetKey(password, keySalt);
  33.         var iv = GenerateRandomBytes(AesBlockByteSize);
  34.  
  35.         byte[] cipherText;
  36.         using (var aes = CreateAes())
  37.         using (var encryptor = aes.CreateEncryptor(key, iv))
  38.         {
  39.             var plainText = StringEncoding.GetBytes(toEncrypt);
  40.             cipherText = encryptor
  41.                 .TransformFinalBlock(plainText, 0, plainText.Length);
  42.         }
  43.  
  44.         // sign
  45.         var authKeySalt = GenerateRandomBytes(PasswordSaltByteSize);
  46.         var authKey = GetKey(password, authKeySalt);
  47.  
  48.         var result = MergeArrays(
  49.             additionalCapacity: SignatureByteSize,
  50.             authKeySalt, keySalt, iv, cipherText);
  51.  
  52.         using (var hmac = new HMACSHA256(authKey))
  53.         {
  54.             var payloadToSignLength = result.Length - SignatureByteSize;
  55.             var signatureTag = hmac.ComputeHash(result, 0, payloadToSignLength);
  56.             signatureTag.CopyTo(result, payloadToSignLength);
  57.         }
  58.  
  59.         return result;
  60.     }
  61.  
  62.     public static string DecryptToString(byte[] encryptedData, string password)
  63.     {
  64.         if (encryptedData is null
  65.             || encryptedData.Length < MinimumEncryptedMessageByteSize)
  66.         {
  67.             throw new ArgumentException("Invalid length of encrypted data");
  68.         }
  69.  
  70.         var authKeySalt = encryptedData
  71.             .AsSpan(0, PasswordSaltByteSize).ToArray();
  72.         var keySalt = encryptedData
  73.             .AsSpan(PasswordSaltByteSize, PasswordSaltByteSize).ToArray();
  74.         var iv = encryptedData
  75.             .AsSpan(2 * PasswordSaltByteSize, AesBlockByteSize).ToArray();
  76.         var signatureTag = encryptedData
  77.             .AsSpan(encryptedData.Length - SignatureByteSize, SignatureByteSize).ToArray();
  78.  
  79.         var cipherTextIndex = authKeySalt.Length + keySalt.Length + iv.Length;
  80.         var cipherTextLength =
  81.             encryptedData.Length - cipherTextIndex - signatureTag.Length;
  82.  
  83.         var authKey = GetKey(password, authKeySalt);
  84.         var key = GetKey(password, keySalt);
  85.  
  86.         // verify signature
  87.         using (var hmac = new HMACSHA256(authKey))
  88.         {
  89.             var payloadToSignLength = encryptedData.Length - SignatureByteSize;
  90.             var signatureTagExpected = hmac
  91.                 .ComputeHash(encryptedData, 0, payloadToSignLength);
  92.  
  93.             // constant time checking to prevent timing attacks
  94.             var signatureVerificationResult = 0;
  95.             for (int i = 0; i < signatureTag.Length; i++)
  96.             {
  97.                 signatureVerificationResult |= signatureTag[i] ^ signatureTagExpected[i];
  98.             }
  99.  
  100.             if (signatureVerificationResult != 0)
  101.             {
  102.                 throw new CryptographicException("Invalid signature");
  103.             }
  104.         }
  105.  
  106.         // decrypt
  107.         using (var aes = CreateAes())
  108.         {
  109.             using (var encryptor = aes.CreateDecryptor(key, iv))
  110.             {
  111.                 var decryptedBytes = encryptor
  112.                     .TransformFinalBlock(encryptedData, cipherTextIndex, cipherTextLength);
  113.                 return StringEncoding.GetString(decryptedBytes);
  114.             }
  115.         }
  116.     }
  117.  
  118.     private static Aes CreateAes()
  119.     {
  120.         var aes = Aes.Create();
  121.         aes.Mode = CipherMode.CBC;
  122.         aes.Padding = PaddingMode.PKCS7;
  123.         return aes;
  124.     }
  125.  
  126.     private static byte[] GetKey(string password, byte[] passwordSalt)
  127.     {
  128.         var keyBytes = StringEncoding.GetBytes(password);
  129.  
  130.         using (var derivator = new Rfc2898DeriveBytes(
  131.             keyBytes, passwordSalt,
  132.             PasswordIterationCount, HashAlgorithmName.SHA256))
  133.         {
  134.             return derivator.GetBytes(PasswordByteSize);
  135.         }
  136.     }
  137.  
  138.     private static byte[] GenerateRandomBytes(int numberOfBytes)
  139.     {
  140.         var randomBytes = new byte[numberOfBytes];
  141.         Random.GetBytes(randomBytes);
  142.         return randomBytes;
  143.     }
  144.  
  145.     private static byte[] MergeArrays(int additionalCapacity = 0, params byte[][] arrays)
  146.     {
  147.         var merged = new byte[arrays.Sum(a => a.Length) + additionalCapacity];
  148.         var mergeIndex = 0;
  149.         for (int i = 0; i < arrays.GetLength(0); i++)
  150.         {
  151.             arrays[i].CopyTo(merged, mergeIndex);
  152.             mergeIndex += arrays[i].Length;
  153.         }
  154.  
  155.         return merged;
  156.     }
  157. }
Add Comment
Please, Sign In to add comment