705 lines
34 KiB
C#
705 lines
34 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace FSI.Lib.EasyEncryption
|
|
{
|
|
/// <summary>
|
|
/// Encryption algorithm types.
|
|
/// </summary>
|
|
public enum EncryptionAlgorithm
|
|
{
|
|
/// <summary>
|
|
/// Specifies the Advanced Encryption Standard (AES) symmetric encryption algorithm.
|
|
/// </summary>
|
|
Aes,
|
|
|
|
/// <summary>
|
|
/// Specifies the Data Encryption Standard (DES) symmetric encryption algorithm.
|
|
/// </summary>
|
|
Des,
|
|
|
|
/// <summary>
|
|
/// Specifies the RC2 symmetric encryption algorithm.
|
|
/// </summary>
|
|
Rc2,
|
|
|
|
/// <summary>
|
|
/// Specifies the Rijndael symmetric encryption algorithm.
|
|
/// </summary>
|
|
Rijndael,
|
|
|
|
/// <summary>
|
|
/// Specifies the TripleDES symmetric encryption algorithm.
|
|
/// </summary>
|
|
TripleDes
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to provide encryption and decryption services.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The Encryption class performs encryption and decryption using the specified algorithm.
|
|
/// </para>
|
|
/// <para>
|
|
/// The encrypted values may appear considerably larger than the decrypted values
|
|
/// because encrypted values contains some additional meta data. If either data size or
|
|
/// performance is a concern, use the <see cref="CreateStreamReader(Stream)"/> or
|
|
/// <see cref="CreateStreamWriter(Stream)"/> methods to work with the streaming classes
|
|
/// instead.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class Encryption
|
|
{
|
|
private static readonly int SaltLength = 8;
|
|
|
|
private static readonly Dictionary<EncryptionAlgorithm, Type> AlgorithmLookup = new Dictionary<EncryptionAlgorithm, Type>
|
|
{
|
|
[EncryptionAlgorithm.Aes] = typeof(Aes),
|
|
[EncryptionAlgorithm.Des] = typeof(DES),
|
|
[EncryptionAlgorithm.Rc2] = typeof(RC2),
|
|
[EncryptionAlgorithm.Rijndael] = typeof(Rijndael),
|
|
[EncryptionAlgorithm.TripleDes] = typeof(TripleDES),
|
|
};
|
|
|
|
private string Password { get; }
|
|
private Type AlgorithmType { get; }
|
|
|
|
/// <summary>
|
|
/// Converts a byte array to a string.
|
|
/// </summary>
|
|
/// <param name="bytes">The byte array to be converted.</param>
|
|
/// <returns>Returns the converted string.</returns>
|
|
public static string EncodeBytesToString(byte[] bytes) => Convert.ToBase64String(bytes);
|
|
|
|
/// <summary>
|
|
/// Converts a string to a byte array.
|
|
/// </summary>
|
|
/// <param name="s">The string to be converted.</param>
|
|
/// <returns>Returns the converted byte array.</returns>
|
|
public static byte[] DecodeBytesFromString(string s) => Convert.FromBase64String(s);
|
|
|
|
/// <summary>
|
|
/// Constructs a new <c>Encryption</c> instance.
|
|
/// </summary>
|
|
/// <param name="password">Specifies the encryption password. Leading and trailing spaces are removed.</param>
|
|
/// <param name="algorithm">Specifies which encryption algorithm is used.</param>
|
|
public Encryption(string password, EncryptionAlgorithm algorithm)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(password))
|
|
throw new ArgumentException("Password is required.", nameof(password));
|
|
Password = password.Trim();
|
|
AlgorithmType = AlgorithmLookup[algorithm];
|
|
}
|
|
|
|
#region Encryption stream creation methods
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="EncryptionWriter"/> instance using the specified stream.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The <see cref="EncryptionWriter"/> class is the preferred method to encrypt data
|
|
/// as it will store some necessary meta data only once for all data in the stream.
|
|
/// It is also more performant. The other encryption methods defer to the EncryptionWriter
|
|
/// class for actual encryption.
|
|
/// </remarks>
|
|
/// <param name="stream">The stream the encrypted data will be written to.</param>
|
|
/// <returns>An instance of the <see cref="EncryptionWriter"/> class.</returns>
|
|
public EncryptionWriter CreateStreamWriter(Stream stream)
|
|
{
|
|
// Create a random salt and write to stream
|
|
byte[] salt = CreateSalt();
|
|
stream.Write(salt, 0, salt.Length);
|
|
// Create symmetric algorithm
|
|
SymmetricAlgorithm algorithm = CreateAlgorithm();
|
|
algorithm.Padding = PaddingMode.PKCS7;
|
|
// Create key and IV
|
|
byte[] key, iv;
|
|
GenerateKeyAndIv(algorithm, salt, out key, out iv);
|
|
// Create EncryptionWriter
|
|
ICryptoTransform encryptor = algorithm.CreateEncryptor(key, iv);
|
|
CryptoStream cs = new CryptoStream(stream, encryptor, CryptoStreamMode.Write);
|
|
return new EncryptionWriter(algorithm, encryptor, stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="EncryptionWriter"/> instance using the specified file name.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The <see cref="EncryptionWriter"/> class is the preferred method to encrypt data
|
|
/// as it will store some necessary meta data only once for all data in the stream.
|
|
/// It is also more performant. The other encryption methods defer to the EncryptionWriter
|
|
/// class for actual encryption.
|
|
/// </remarks>
|
|
/// <param name="path">The file name the encrypted data will be written to.</param>
|
|
/// <returns>An instance of the <see cref="EncryptionWriter"/> class.</returns>
|
|
public EncryptionWriter CreateStreamWriter(string path)
|
|
{
|
|
return CreateStreamWriter(File.Open(path, FileMode.OpenOrCreate, FileAccess.Write));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="EncryptionReader"/> instance using the specified stream.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The <see cref="EncryptionReader"/> class is the preferred method to decrypt data.
|
|
/// It is also more performant. The other decryption methods defer to the EncryptionReader
|
|
/// class for actual decryption.
|
|
/// </remarks>
|
|
/// <param name="stream">The stream the encrypted data will be read from.</param>
|
|
/// <returns>An instance of the <see cref="EncryptionReader"/> class.</returns>
|
|
public EncryptionReader CreateStreamReader(Stream stream)
|
|
{
|
|
// Read salt from input stream
|
|
byte[] salt = new byte[SaltLength];
|
|
if (stream.Read(salt, 0, salt.Length) < SaltLength)
|
|
throw new ArgumentOutOfRangeException("Reached end of input stream before reading encryption metadata.");
|
|
// Create symmetric algorithm
|
|
SymmetricAlgorithm algorithm = CreateAlgorithm();
|
|
algorithm.Padding = PaddingMode.PKCS7;
|
|
// Create key and IV
|
|
byte[] key, iv;
|
|
GenerateKeyAndIv(algorithm, salt, out key, out iv);
|
|
// Create EncryptionReader
|
|
ICryptoTransform decryptor = algorithm.CreateDecryptor(key, iv);
|
|
CryptoStream cs = new CryptoStream(stream, decryptor, CryptoStreamMode.Read);
|
|
return new EncryptionReader(algorithm, decryptor, stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="EncryptionReader"/> instance using the specified file name.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The <see cref="EncryptionReader"/> class is the preferred method to decrypt data.
|
|
/// It is also more performant. The other decryption methods defer to the EncryptionReader
|
|
/// class for actual decryption.
|
|
/// </remarks>
|
|
/// <param name="path">The file name the encrypted data will be read from.</param>
|
|
/// <returns>An instance of the <see cref="EncryptionReader"/> class.</returns>
|
|
public EncryptionReader CreateStreamReader(string path)
|
|
{
|
|
return CreateStreamReader(File.Open(path, FileMode.Open, FileAccess.Read));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Encryption
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>string</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(string value) => Encrypt(w => w.Write(value ?? string.Empty));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>bool</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(bool value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>char</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(char value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>sbyte</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(sbyte value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>byte</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(byte value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>short</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(short value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>ushort</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(ushort value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>int</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(int value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>uint</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(uint value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>long</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(long value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>ulong</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(ulong value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>float</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(float value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>double</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(double value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>decimal</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(decimal value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>DateTime</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(DateTime value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>byte[]</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(byte[] value) => Encrypt(w => w.Write(value));
|
|
|
|
/// <summary>
|
|
/// Encrypts a <c>string[]</c> value.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encrypted value will be larger than the unencrypted value because the
|
|
/// encrypted value will contain some additional meta data. To minimize the
|
|
/// size of this meta data when encrypting multiple values, use <see cref="CreateStreamWriter(Stream)"/>
|
|
/// to create a <see cref="EncryptionWriter"/> instead.
|
|
/// </remarks>
|
|
/// <param name="value">The value to decrypt.</param>
|
|
/// <returns>Returns the encrypted value</returns>
|
|
public string Encrypt(string[] value) => Encrypt(w => w.Write(value));
|
|
|
|
private string Encrypt(Action<EncryptionWriter> action)
|
|
{
|
|
using (MemoryStream stream = new MemoryStream())
|
|
using (EncryptionWriter writer = CreateStreamWriter(stream))
|
|
{
|
|
action(writer);
|
|
return EncodeBytesToString(stream.ToArray());
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Decryption
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>string</c> value encrypted with <see cref="Encrypt(string)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public string DecryptString(string encryptedValue) => (string)Decrypt(encryptedValue, r => r.ReadString());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>bool</c> value encrypted with <see cref="Encrypt(bool)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public bool DecryptBoolean(string encryptedValue) => (bool)Decrypt(encryptedValue, r => r.ReadBoolean());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>char</c> value encrypted with <see cref="Encrypt(char)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public char DecryptChar(string encryptedValue) => (char)Decrypt(encryptedValue, r => r.ReadChar());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>sbyte</c> value encrypted with <see cref="Encrypt(sbyte)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public sbyte DecryptSByte(string encryptedValue) => (sbyte)Decrypt(encryptedValue, r => r.ReadSByte());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>byte</c> value encrypted with <see cref="Encrypt(byte)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public byte DecryptByte(string encryptedValue) => (byte)Decrypt(encryptedValue, r => r.ReadByte());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>short</c> value encrypted with <see cref="Encrypt(short)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public short DecryptInt16(string encryptedValue) => (short)Decrypt(encryptedValue, r => r.ReadInt16());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>ushort</c> value encrypted with <see cref="Encrypt(ushort)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public ushort DecryptUInt16(string encryptedValue) => (ushort)Decrypt(encryptedValue, r => r.ReadUInt16());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>int</c> value encrypted with <see cref="Encrypt(int)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public int DecryptInt32(string encryptedValue) => (int)Decrypt(encryptedValue, r => r.ReadInt32());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>uint</c> value encrypted with <see cref="Encrypt(uint)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public uint DecryptUInt32(string encryptedValue) => (uint)Decrypt(encryptedValue, r => r.ReadUInt32());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>long</c> value encrypted with <see cref="Encrypt(long)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public long DecryptInt64(string encryptedValue) => (long)Decrypt(encryptedValue, r => r.ReadInt64());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>ulong</c> value encrypted with <see cref="Encrypt(ulong)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public ulong DecryptUInt64(string encryptedValue) => (ulong)Decrypt(encryptedValue, r => r.ReadUInt64());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>float</c> value encrypted with <see cref="Encrypt(float)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public float DecryptSingle(string encryptedValue) => (float)Decrypt(encryptedValue, r => r.ReadSingle());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>double</c> value encrypted with <see cref="Encrypt(double)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public double DecryptDouble(string encryptedValue) => (double)Decrypt(encryptedValue, r => r.ReadDouble());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>decimal</c> value encrypted with <see cref="Encrypt(decimal)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public decimal DecryptDecimal(string encryptedValue) => (decimal)Decrypt(encryptedValue, r => r.ReadDecimal());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>DateTime</c> value encrypted with <see cref="Encrypt(DateTime)"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public DateTime DecryptDateTime(string encryptedValue) => (DateTime)Decrypt(encryptedValue, r => r.ReadDateTime());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>byte[]</c> value encrypted with <see cref="Encrypt(byte[])"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public byte[] DecryptByteArray(string encryptedValue) => (byte[])Decrypt(encryptedValue, r => r.ReadByteArray());
|
|
|
|
/// <summary>
|
|
/// Decrypts a <c>string[]</c> value encrypted with <see cref="Encrypt(string[])"/>.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The value to decrypt.</param>
|
|
/// <returns>Returns the decrypted value</returns>
|
|
public string[] DecryptStringArray(string encryptedValue) => (string[])Decrypt(encryptedValue, r => r.ReadStringArray());
|
|
|
|
private object Decrypt(string encryptedValue, Func<EncryptionReader, object> action)
|
|
{
|
|
using (MemoryStream stream = new MemoryStream(DecodeBytesFromString(encryptedValue)))
|
|
using (EncryptionReader reader = CreateStreamReader(stream))
|
|
{
|
|
return action(reader);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Encryption/decryption of objects
|
|
|
|
/// <summary>
|
|
/// Class to hold encrypt/decrypt functions for each supported data type.
|
|
/// </summary>
|
|
private class TypeInfo
|
|
{
|
|
public Func<Encryption, object, string> Encrypt { get; set; }
|
|
public Func<Encryption, string, object> Decrypt { get; set; }
|
|
|
|
public TypeInfo(Func<Encryption, object, string> encrypt, Func<Encryption, string, object> decrypt)
|
|
{
|
|
Encrypt = encrypt;
|
|
Decrypt = decrypt;
|
|
}
|
|
}
|
|
|
|
private static readonly Dictionary<Type, TypeInfo> TypeInfoLookup = new Dictionary<Type, TypeInfo>()
|
|
{
|
|
[typeof(string)] = new TypeInfo((e, v) => e.Encrypt((string)v), (e, s) => e.DecryptString(s)),
|
|
[typeof(bool)] = new TypeInfo((e, v) => e.Encrypt((bool)v), (e, s) => e.DecryptBoolean(s)),
|
|
[typeof(char)] = new TypeInfo((e, v) => e.Encrypt((char)v), (e, s) => e.DecryptChar(s)),
|
|
[typeof(sbyte)] = new TypeInfo((e, v) => e.Encrypt((sbyte)v), (e, s) => e.DecryptSByte(s)),
|
|
[typeof(byte)] = new TypeInfo((e, v) => e.Encrypt((byte)v), (e, s) => e.DecryptByte(s)),
|
|
[typeof(short)] = new TypeInfo((e, v) => e.Encrypt((short)v), (e, s) => e.DecryptInt16(s)),
|
|
[typeof(ushort)] = new TypeInfo((e, v) => e.Encrypt((ushort)v), (e, s) => e.DecryptUInt16(s)),
|
|
[typeof(int)] = new TypeInfo((e, v) => e.Encrypt((int)v), (e, s) => e.DecryptInt32(s)),
|
|
[typeof(uint)] = new TypeInfo((e, v) => e.Encrypt((uint)v), (e, s) => e.DecryptUInt32(s)),
|
|
[typeof(long)] = new TypeInfo((e, v) => e.Encrypt((long)v), (e, s) => e.DecryptInt64(s)),
|
|
[typeof(ulong)] = new TypeInfo((e, v) => e.Encrypt((ulong)v), (e, s) => e.DecryptUInt64(s)),
|
|
[typeof(float)] = new TypeInfo((e, v) => e.Encrypt((float)v), (e, s) => e.DecryptSingle(s)),
|
|
[typeof(double)] = new TypeInfo((e, v) => e.Encrypt((double)v), (e, s) => e.DecryptDouble(s)),
|
|
[typeof(decimal)] = new TypeInfo((e, v) => e.Encrypt((decimal)v), (e, s) => e.DecryptDecimal(s)),
|
|
[typeof(DateTime)] = new TypeInfo((e, v) => e.Encrypt((DateTime)v), (e, s) => e.DecryptDateTime(s)),
|
|
[typeof(byte[])] = new TypeInfo((e, v) => e.Encrypt((byte[])v), (e, s) => e.DecryptByteArray(s)),
|
|
[typeof(string[])] = new TypeInfo((e, v) => e.Encrypt((string[])v), (e, s) => e.DecryptStringArray(s)),
|
|
};
|
|
|
|
/// <summary>
|
|
/// Indicates if the specified data type is supported by the encryption and decryption methods.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The encryption code supports all basic .NET data types in addition to <c>byte[]</c>
|
|
/// and <c>string[]</c>. More complex data types are not supported.
|
|
/// </remarks>
|
|
/// <param name="type">The data type to be tested.</param>
|
|
/// <returns>True if the specified type is supported. False otherwise.</returns>
|
|
public static bool IsTypeSupported(Type type) => TypeInfoLookup.ContainsKey(type);
|
|
|
|
/// <summary>
|
|
/// Encrypts an object value. The object must hold one of the supported data types.
|
|
/// </summary>
|
|
/// <param name="value">Object to be encrypted.</param>
|
|
/// <exception cref="ArgumentException"><paramref name="value"/> holds an unsupported data type.</exception>
|
|
/// <returns>An encrypted string that can be decrypted using Decrypt.</returns>
|
|
public string Encrypt(object value)
|
|
{
|
|
if (value == null)
|
|
return null;
|
|
if (TypeInfoLookup.TryGetValue(value.GetType(), out TypeInfo info))
|
|
return info.Encrypt(this, value);
|
|
throw new ArgumentException(string.Format("Cannot encrypt value : Data type '{0}' is not supported", value.GetType()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypts an object value of the specified type.
|
|
/// </summary>
|
|
/// <param name="encryptedValue">The encrypted string to be decrypted.</param>
|
|
/// <param name="targetType">The type of data that was originally encrypted into <paramref name="encryptedValue"/>.</param>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
/// <returns>Returns the decrypted value.</returns>
|
|
public object Decrypt(string encryptedValue, Type targetType)
|
|
{
|
|
if (TypeInfoLookup.TryGetValue(targetType, out TypeInfo info))
|
|
return info.Decrypt(this, encryptedValue);
|
|
throw new ArgumentException(string.Format("Cannot decrypt value : Data type '{0}' is not supported", targetType));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Support methods
|
|
|
|
/// <summary>
|
|
/// Creates a SymmetricAlgorithm instance for the current encryption algorithm.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns the created SymmetricAlgorithm instance.
|
|
/// </returns>
|
|
protected SymmetricAlgorithm CreateAlgorithm()
|
|
{
|
|
MethodInfo method = AlgorithmType.GetMethod("Create", Array.Empty<Type>());
|
|
if (method != null)
|
|
{
|
|
if (method.Invoke(null, null) is SymmetricAlgorithm algorithm)
|
|
return algorithm;
|
|
}
|
|
throw new Exception($"Unable to create instance of {AlgorithmType.FullName}.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a salt that contains a cryptographically strong sequence of random values.
|
|
/// </summary>
|
|
/// <returns>The generated salt value.</returns>
|
|
private byte[] CreateSalt()
|
|
{
|
|
byte[] salt = new byte[SaltLength];
|
|
using (RNGCryptoServiceProvider generator = new RNGCryptoServiceProvider())
|
|
{
|
|
generator.GetBytes(salt);
|
|
}
|
|
return salt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a pseudorandom key and initialization vector from the current password and the
|
|
/// given salt.
|
|
/// </summary>
|
|
/// <param name="algorithm"><see cref="SymmetricAlgorithm"></see> being used to encrypt.</param>
|
|
/// <param name="salt">The salt used to derive the key and initialization vector.</param>
|
|
/// <param name="key">Returns the generated key.</param>
|
|
/// <param name="iv">Returns the generated initialization vector.</param>
|
|
protected void GenerateKeyAndIv(SymmetricAlgorithm algorithm, byte[] salt, out byte[] key, out byte[] iv)
|
|
{
|
|
int keyLength = algorithm.KeySize >> 3;
|
|
int ivLength = algorithm.BlockSize >> 3;
|
|
byte[] bytes = DeriveBytes(salt, keyLength + ivLength);
|
|
key = new byte[keyLength];
|
|
Buffer.BlockCopy(bytes, 0, key, 0, keyLength);
|
|
iv = new byte[ivLength];
|
|
Buffer.BlockCopy(bytes, keyLength, iv, 0, ivLength);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a series of pseudorandom bytes of the specified length based on the current
|
|
/// password and the given salt.
|
|
/// </summary>
|
|
/// <param name="salt">The salt used to derive the bytes.</param>
|
|
/// <param name="bytes">The number of bits of data to generate.</param>
|
|
/// <returns>Returns the derived bytes.</returns>
|
|
protected byte[] DeriveBytes(byte[] salt, int bytes)
|
|
{
|
|
Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(Password, salt, 1000);
|
|
return derivedBytes.GetBytes(bytes);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|