WingMan – Rev 35
?pathlinks?
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace WingMan.Utilities
{
public static class AES
{
private const int AesKeyIterations = 4096;
private const int AesBlockSize = 128;
private const CipherMode AesCipherMode = CipherMode.CBC;
private const PaddingMode AesPaddingMode = PaddingMode.PKCS7;
private const int AesKeySaltBytes = 16;
private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider();
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Encrypts an input stream given a key and initialization vector.
/// </summary>
/// <param name="inputStream">the stream to encrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>an encrypted stream containing the salt and the data</returns>
public static async Task<MemoryStream> Encrypt(Stream inputStream, string key)
{
var outputStream = new MemoryStream();
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
await inputStream.CopyToAsync(cryptoStream);
cryptoStream.FlushFinalBlock();
memoryStream.Position = 0L;
await outputStream.WriteAsync(salt, 0, AesKeySaltBytes);
await memoryStream.CopyToAsync(outputStream);
outputStream.Position = 0L;
return outputStream;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Encrypts a byte array given a key and initialization vector.
/// </summary>
/// <param name="data">the byte array to encrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>an encrypted byte array</returns>
public static async Task<byte[]> Encrypt(byte[] data, string key)
{
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var inputStream = new MemoryStream(data))
{
await inputStream.CopyToAsync(cryptoStream);
cryptoStream.FlushFinalBlock();
memoryStream.Position = 0L;
return salt.Concat(memoryStream.ToArray()).ToArray();
}
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decrypts a byte array using a salt and a key.
/// </summary>
/// <param name="inputStream">a salt and data stream to decrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>a memory stream containing the decrypted data</returns>
public static async Task<MemoryStream> Decrypt(Stream inputStream, string key)
{
var outputStream = new MemoryStream();
var salt = new byte[AesKeySaltBytes];
await inputStream.ReadAsync(salt, 0, AesKeySaltBytes);
var text = new byte[inputStream.Length - AesKeySaltBytes];
await inputStream.ReadAsync(text, 0, (int) (inputStream.Length - AesKeySaltBytes));
//var salt = data.Take(AesKeySaltBytes).ToArray();
//var text = data.Skip(AesKeySaltBytes).ToArray();
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream(text))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(outputStream);
outputStream.Position = 0L;
return outputStream;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decrypts a byte array using a salt and a key.
/// </summary>
/// <param name="data">a salt and data byte array to decrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>a byte array containing the decrypted data</returns>
public static async Task<byte[]> Decrypt(byte[] data, string key)
{
var salt = data.Take(AesKeySaltBytes).ToArray();
var text = data.Skip(AesKeySaltBytes).ToArray();
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream(text))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var outputStream = new MemoryStream())
{
await cryptoStream.CopyToAsync(outputStream);
outputStream.Position = 0L;
return outputStream.ToArray();
}
}
}
}
}
}
public static string ExpandKey(string password, int size = 32)
{
var sb = new StringBuilder(password);
do
{
sb.Append(password);
} while (sb.Length < size);
return sb.ToString(0, size);
}
}
}