Squashed 'FSI.Lib/' content from commit 6aa4846
git-subtree-dir: FSI.Lib git-subtree-split: 6aa48465a834a7bfdd9cbeae8d2e4f769d0c0ff8
This commit is contained in:
704
FSI.Lib/EasyEncryption/Encryption.cs
Normal file
704
FSI.Lib/EasyEncryption/Encryption.cs
Normal file
@@ -0,0 +1,704 @@
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user