C# symetric async encrypt/decrypt class.
namespace Utils
{
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
/// <summary>
/// Async encrypt/decrypt class.
/// </summary>
/// <remarks>https://github.com/IntelliTect/EssentialCSharp/blob/v8.0/src/Shared/Cryptographer.cs</remarks>
public class Encryptor : IDisposable
{
/// <inheritdoc cref="SymmetricAlgorithm" />
public SymmetricAlgorithm CryptoAlgorithm { get; }
public Encryptor(SymmetricAlgorithm cryptoAlgorithm)
{
CryptoAlgorithm = cryptoAlgorithm;
}
public Encryptor()
: this(Aes.Create()) { }
public byte[] Encrypt(string text)
{
return EncryptAsync(text).Result;
}
public async Task<byte[]> EncryptAsync(string text)
{
return await EncryptAsync(text, CryptoAlgorithm.CreateEncryptor());
}
public async Task<byte[]> EncryptAsync(string text, Stream outputFileStream)
{
return await EncryptAsync(text, CryptoAlgorithm.CreateEncryptor(), outputFileStream);
}
public static async Task<byte[]> EncryptAsync(string plainText, byte[] key, byte[] iv)
{
byte[] encrypted;
using (var aes = new AesManaged())
{
var encryptor = aes.CreateEncryptor(key, iv);
encrypted = await EncryptAsync(plainText, encryptor);
}
return encrypted;
}
private static async Task<byte[]> EncryptAsync(string plainText, ICryptoTransform encryptor)
{
using var memoryStream = new MemoryStream();
return await EncryptAsync(plainText, encryptor, memoryStream);
}
private static async Task<byte[]> EncryptAsync(
string plainText,
ICryptoTransform encryptor,
Stream outputStream
)
{
byte[] encrypted;
MemoryStream memoryStream;
// Create crypto stream using the CryptoStream class. This class is the key to encryption
// and encrypts and decrypts data from any given stream. In this case, we will pass a memory stream
// to encrypt
using (var cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write))
{
// Create StreamWriter and write data to a stream
using (var streamWriter = new StreamWriter(cryptoStream))
{
await streamWriter.WriteAsync(plainText);
if (outputStream is MemoryStream stream)
{
streamWriter.Close();
memoryStream = stream;
}
else
{
using (memoryStream = new MemoryStream())
{
await outputStream.CopyToAsync(memoryStream);
}
}
encrypted = memoryStream.ToArray();
}
}
return encrypted;
}
public string Decrypt(byte[] encryptedData)
{
return DecryptAsync(encryptedData).Result;
}
public async Task<string> DecryptAsync(byte[] encryptedData)
{
return await DecryptAsync(encryptedData, CryptoAlgorithm.CreateDecryptor());
}
public async Task DecryptAsync(byte[] encryptedData, Stream outputStream)
{
await DecryptAsync(encryptedData, CryptoAlgorithm.CreateDecryptor(), outputStream);
}
public static async Task<string> DecryptAsync(byte[] encryptedData, byte[] key, byte[] iV)
{
using (var aes = new AesManaged())
{
var decryptor = aes.CreateDecryptor(key, iV);
var plaintext = await DecryptAsync(encryptedData, decryptor);
return plaintext;
}
}
private static async Task<string> DecryptAsync(byte[] encryptedData, ICryptoTransform decryptor)
{
using var encryptedStream = new MemoryStream(encryptedData);
{
return await DecryptAsync(encryptedStream, decryptor);
}
}
private static async Task DecryptAsync(byte[] encryptedData, ICryptoTransform decryptor, Stream outputStream)
{
using (var encryptedStream = new MemoryStream(encryptedData))
{
await DecryptAsync(encryptedStream, decryptor, outputStream);
}
}
private static async Task<string> DecryptAsync(Stream encryptedStream, ICryptoTransform decryptor)
{
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
using (var reader = new StreamReader(cryptoStream))
{
return await reader.ReadToEndAsync();
}
}
}
private static async Task DecryptAsync(
Stream encryptedStream,
ICryptoTransform decryptor,
Stream decryptedStream
)
{
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(decryptedStream);
}
}
private bool Disposed { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (Disposed)
{
return;
}
if (disposing)
{
if (CryptoAlgorithm != null)
{
CryptoAlgorithm.Clear();
CryptoAlgorithm.Dispose();
}
}
Disposed = true;
}
}
}
//
// Unit Tests
// ---------------------------------------------------------------------------
namespace Tests.Utils
{
using Xunit;
using System.Security.Cryptography;
using Utils;
public class CryptographerTests
{
public static byte[] GenerateSecureRandomKey(int byteSize, bool getNonZeroBytesOnly = false)
{
using (var rng = new RNGCryptoServiceProvider())
{
var key = new byte[byteSize];
if (getNonZeroBytesOnly)
{
rng.GetNonZeroBytes(key);
}
else
{
rng.GetBytes(key);
}
return key;
}
}
[Fact]
public void EncryptAesManaged()
{
const string plainText =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor " +
"incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " +
"exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit " +
"anim id est laborum.";
using (var aes = new AesManaged
{
Key = CryptoUtil.GenerateSecureRandomKey(byteSize: 32),
IV = CryptoUtil.GenerateSecureRandomKey(byteSize: 16),
Padding = PaddingMode.PKCS7, // also the default
Mode = CipherMode.CBC, // also the default
KeySize = 256, // also the default
BlockSize = 128 // also the default
})
{
using (var encryptor = new Encryptor(aes))
{
var encrypted = encryptor.EncryptAsync(plainText).Result;
var decrypted = encryptor.DecryptAsync(encrypted).Result;
Assert.Equal(plainText, decrypted);
}
}
}
}
}