using System.IO;
using System.Text;
namespace System.Security.Cryptography
{
///
/// Provides cryptographic functions ready-to-use.
///
public class CryptographyHelper
{
private static int saltLength = 8;
private readonly string masterKeyFile;
///
/// Initializes a new instance of the class.
///
/// The (absolute) path to the crypto key file. On null the file 'crypto.key' at the executing assembly location will be used.
public CryptographyHelper(string keyFile = null)
{
masterKeyFile = keyFile;
if (string.IsNullOrWhiteSpace(masterKeyFile))
masterKeyFile = "crypto.key";
if (!Path.IsPathRooted(masterKeyFile))
masterKeyFile = Path.Combine(AppContext.BaseDirectory, masterKeyFile);
string pw = File.Exists(masterKeyFile) ? File.ReadAllText(masterKeyFile) : null;
if (string.IsNullOrWhiteSpace(pw))
File.WriteAllText(masterKeyFile, GetRandomString(64));
}
#region Instance methods
#region AES
///
/// Decrypts data using the AES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The encrypted data (cipher).
/// The password to use for decryption (optional).
/// The decrypted data.
public byte[] DecryptAes(byte[] cipher, string password = null)
{
if (password == null)
password = File.ReadAllText(masterKeyFile);
return AesDecrypt(cipher, password);
}
///
/// Encrypts data using the AES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The data to encrypt.
/// The password to use for encryption (optional).
/// The encrypted data (cipher).
public byte[] EncryptAes(byte[] plain, string password = null)
{
if (password == null)
password = File.ReadAllText(masterKeyFile);
return AesEncrypt(plain, password);
}
///
/// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
///
/// The encrypted Base64 encoded string.
/// The password to use for decryption (optional).
/// The decrypted UTF-8 string string.
public string DecryptAes(string cipherStr, string password = null)
{
byte[] cipher = Convert.FromBase64String(cipherStr);
byte[] plain = DecryptAes(cipher, password);
return Encoding.UTF8.GetString(plain);
}
///
/// Encrypts an UTF-8 string using the AES algorithm and a password to a Base64 string.
///
/// The UTF-8 string to encrypt.
/// The password to use for encryption (optional).
/// The encrypted Base64 encoded string.
public string EncryptAes(string plainStr, string password = null)
{
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
byte[] cipher = EncryptAes(plain, password);
return Convert.ToBase64String(cipher);
}
#endregion AES
#region Triple DES
///
/// Decrypts data using the triple DES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The encrypted data (cipher).
/// The password to use for decryption (optional).
/// The decrypted data.
public byte[] DecryptTripleDes(byte[] cipher, string password = null)
{
if (password == null)
password = File.ReadAllText(masterKeyFile);
return TripleDesDecrypt(cipher, password);
}
///
/// Encrypts data using the triple DES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The data to encrypt.
/// The password to use for encryption (optional).
/// The encrypted data (cipher).
public byte[] EncryptTripleDes(byte[] plain, string password = null)
{
if (password == null)
password = File.ReadAllText(masterKeyFile);
return TripleDesEncrypt(plain, password);
}
///
/// Decrypts a Base64 encoded string using the triple DES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The encrypted Base64 encoded string.
/// The password to use for decryption (optional).
/// The decrypted UTF-8 string.
public string DecryptTripleDes(string cipherStr, string password = null)
{
byte[] cipher = Convert.FromBase64String(cipherStr);
byte[] plain = DecryptTripleDes(cipher, password);
return Encoding.UTF8.GetString(plain);
}
///
/// Encrypts an UTF-8 string using the triple DES algorithm and a password.
///
///
/// When the parameter is null, the key from the file (set on initialize) is used instead.
///
/// The UTF-8 string to encrypt.
/// The password to use for encryption (optional).
/// The encrypted Base64 encoded string.
public string EncryptTripleDes(string plainStr, string password = null)
{
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
byte[] cipher = EncryptTripleDes(plain, password);
return Convert.ToBase64String(cipher);
}
#endregion Triple DES
#endregion Instance methods
#region Static methods
#region Encryption
#region AES
///
/// Decrypts data using the AES algorithm and a password.
///
/// The encrypted data (cipher).
/// The password to use for decryption.
/// The decrypted data.
public static byte[] AesDecrypt(byte[] cipher, string password)
{
byte[] salt = new byte[saltLength];
Array.Copy(cipher, salt, saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt);
using var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = gen.GetBytes(aes.KeySize / 8);
aes.IV = gen.GetBytes(aes.BlockSize / 8);
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipher, saltLength, cipher.Length - saltLength);
cs.FlushFinalBlock();
return ms.ToArray();
}
///
/// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
///
/// The encrypted Base64 encoded string.
/// The password to use for decryption.
/// The decrypted UTF-8 string string.
public static string AesDecrypt(string cipherStr, string password)
{
byte[] cipher = Convert.FromBase64String(cipherStr);
byte[] plain = AesDecrypt(cipher, password);
return Encoding.UTF8.GetString(plain);
}
///
/// Encrypts data using the AES algorithm and a password.
///
/// The data to encrypt.
/// The password to use for encryption.
/// The encrypted data (cipher).
public static byte[] AesEncrypt(byte[] plain, string password)
{
byte[] salt = GetRandomBytes(saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt);
using var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = gen.GetBytes(aes.KeySize / 8);
aes.IV = gen.GetBytes(aes.BlockSize / 8);
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
ms.Write(salt, 0, salt.Length);
cs.Write(plain, 0, plain.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
///
/// Encrypts an UTF-8 string using the AES algorithm and a password to a Base64 string.
///
/// The UTF-8 string to encrypt.
/// The password to use for encryption.
/// The encrypted Base64 encoded string.
public static string AesEncrypt(string plainStr, string password)
{
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
byte[] cipher = AesEncrypt(plain, password);
return Convert.ToBase64String(cipher);
}
#endregion AES
#region Triple DES
///
/// Decrypts data using the triple DES algorithm and a password.
///
/// The encrypted data (cipher).
/// The password to use for decryption.
/// The decrypted data.
public static byte[] TripleDesDecrypt(byte[] cipher, string password)
{
byte[] salt = new byte[saltLength];
Array.Copy(cipher, salt, saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt);
using var tdes = TripleDES.Create();
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.PKCS7;
tdes.Key = gen.GetBytes(tdes.KeySize / 8);
tdes.IV = gen.GetBytes(tdes.BlockSize / 8);
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, tdes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipher, saltLength, cipher.Length - saltLength);
cs.FlushFinalBlock();
return ms.ToArray();
}
///
/// Encrypts data using the triple DES algorithm and a password.
///
/// The data to encrypt.
/// The password to use for encryption.
/// The encrypted data (cipher).
public static byte[] TripleDesEncrypt(byte[] plain, string password)
{
byte[] salt = GetRandomBytes(saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt);
using var tdes = TripleDES.Create();
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.PKCS7;
tdes.Key = gen.GetBytes(tdes.KeySize / 8);
tdes.IV = gen.GetBytes(tdes.BlockSize / 8);
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, tdes.CreateEncryptor(), CryptoStreamMode.Write);
ms.Write(salt, 0, salt.Length);
cs.Write(plain, 0, plain.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
///
/// Decrypts an Base64 encoded string using the triple DES algorithm and a password.
///
/// The encrypted Base64 encoded string.
/// The password to use for decryption.
/// The decrypted UTF-8 string.
public static string TripleDesDecrypt(string cipherStr, string password)
{
byte[] cipher = Convert.FromBase64String(cipherStr);
byte[] plain = TripleDesDecrypt(cipher, password);
return Encoding.UTF8.GetString(plain);
}
///
/// Encrypts an UTF-8 string using the triple DES algorithm and a password.
///
/// The UTF-8 string to encrypt.
/// The password to use for encryption.
/// The encrypted Base64 encoded string.
public static string TripleDesEncrypt(string plainStr, string password)
{
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
byte[] cipher = TripleDesEncrypt(plain, password);
return Convert.ToBase64String(cipher);
}
#endregion Triple DES
#endregion Encryption
#region Hashing
#region MD5
///
/// Computes a hash value from a string using the MD5 algorithm.
///
/// The string to hash, using UTF-8 encoding.
/// The MD5 hash value, in hexadecimal notation.
public static string Md5(string str)
{
return Md5(Encoding.UTF8.GetBytes(str));
}
///
/// Computes a hash value from a file using the MD5 algorithm.
///
/// The name of the file to read.
/// The MD5 hash value, in hexadecimal notation.
public static string Md5File(string fileName)
{
using var md5 = MD5.Create();
using var fs = new FileStream(fileName, FileMode.Open);
return md5.ComputeHash(fs).BytesToHex();
}
///
/// Computes a hash from a byte array value using the MD5 algorithm.
///
/// The byte array.
/// The MD5 hash value, in hexadecimal notation.
public static string Md5(byte[] bytes)
{
using var md5 = MD5.Create();
return md5.ComputeHash(bytes).BytesToHex();
}
#endregion MD5
#region SHA-1
///
/// Computes a hash value from a string using the SHA-1 algorithm.
///
/// The string to hash, using UTF-8 encoding.
/// The SHA-1 hash value, in hexadecimal notation.
public static string Sha1(string str)
{
return Sha1(Encoding.UTF8.GetBytes(str));
}
///
/// Computes a hash value from a file using the SHA-1 algorithm.
///
/// The name of the file to read.
/// The SHA-1 hash value, in hexadecimal notation.
public static string Sha1File(string fileName)
{
using var sha1 = SHA1.Create();
using var fs = new FileStream(fileName, FileMode.Open);
return sha1.ComputeHash(fs).BytesToHex();
}
///
/// Computes a hash from a byte array value using the SHA-1 algorithm.
///
/// The byte array.
/// The SHA-1 hash value, in hexadecimal notation.
public static string Sha1(byte[] bytes)
{
using var sha1 = SHA1.Create();
return sha1.ComputeHash(bytes).BytesToHex();
}
#endregion SHA-1
#region SHA-256
///
/// Computes a hash value from a string using the SHA-256 algorithm.
///
/// The string to hash, using UTF-8 encoding.
/// The SHA-256 hash value, in hexadecimal notation.
public static string Sha256(string str)
{
return Sha256(Encoding.UTF8.GetBytes(str));
}
///
/// Computes a hash value from a file using the SHA-256 algorithm.
///
/// The name of the file to read.
/// The SHA-256 hash value, in hexadecimal notation.
public static string Sha256File(string fileName)
{
using var sha256 = SHA256.Create();
using var fs = new FileStream(fileName, FileMode.Open);
return sha256.ComputeHash(fs).BytesToHex();
}
///
/// Computes a hash from a byte array value using the SHA-256 algorithm.
///
/// The byte array.
/// The SHA-256 hash value, in hexadecimal notation.
public static string Sha256(byte[] bytes)
{
using var sha256 = SHA256.Create();
return sha256.ComputeHash(bytes).BytesToHex();
}
#endregion SHA-256
#region SHA-512
///
/// Computes a hash value from a string using the SHA-512 algorithm.
///
/// The string to hash, using UTF-8 encoding.
/// The SHA-512 hash value, in hexadecimal notation.
public static string Sha512(string str)
{
return Sha512(Encoding.UTF8.GetBytes(str));
}
///
/// Computes a hash value from a file using the SHA-512 algorithm.
///
/// The name of the file to read.
/// The SHA-512 hash value, in hexadecimal notation.
public static string Sha512File(string fileName)
{
using var sha512 = SHA512.Create();
using var fs = new FileStream(fileName, FileMode.Open);
return sha512.ComputeHash(fs).BytesToHex();
}
///
/// Computes a hash from a byte array value using the SHA-512 algorithm.
///
/// The byte array.
/// The SHA-512 hash value, in hexadecimal notation.
public static string Sha512(byte[] bytes)
{
using var sha512 = SHA512.Create();
return sha512.ComputeHash(bytes).BytesToHex();
}
#endregion SHA-512
#endregion Hashing
#region Random
///
/// Generates an array with random (non-zero) bytes.
///
/// The number of bytes to generate.
///
public static byte[] GetRandomBytes(int count)
{
using var gen = RandomNumberGenerator.Create();
byte[] bytes = new byte[count];
gen.GetNonZeroBytes(bytes);
return bytes;
}
///
/// Generates a string with random characters.
///
/// The length of the string to generate.
/// The characters to use (Default: [a-zA-Z0-9]).
///
public static string GetRandomString(int length, string pool = null)
{
if (string.IsNullOrWhiteSpace(pool))
pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var sb = new StringBuilder(length);
int multiply = sizeof(int) / sizeof(byte);
int len = length * multiply;
byte[] bytes = GetRandomBytes(len);
for (int i = 0; i < bytes.Length; i += multiply)
{
uint number = BitConverter.ToUInt32(bytes, i);
sb.Append(pool[(int)(number % pool.Length)]);
}
return sb.ToString();
}
#endregion Random
#region Secure probing
///
/// Determines whether two byte arrays are equal in constant time. This method does not stop
/// early if a difference was detected, unless the length differs.
///
/// The first array.
/// The second array.
/// true, if both arrays are equal; otherwise, false.
public static bool SecureEquals(byte[] a, byte[] b)
{
if ((a == null) != (b == null))
return false;
if (a.Length != b.Length)
return false;
int differentBits = 0;
for (int i = 0; i < a.Length; i++)
{
differentBits |= a[i] ^ b[i];
}
return differentBits == 0;
}
///
/// Determines whether two strings are equal in constant time. This method does not stop
/// early if a difference was detected, unless the length differs.
///
/// The first string.
/// The second string.
/// true, if both strings are equal; otherwise, false.
public static bool SecureEquals(string a, string b)
{
if ((a == null) != (b == null))
return false;
if (a.Length != b.Length)
return false;
int differentBits = 0;
for (int i = 0; i < a.Length; i++)
{
differentBits |= a[i] ^ b[i];
}
return differentBits == 0;
}
#endregion Secure probing
#endregion Static methods
}
}