Files
SteamTools/System.Common.CoreLib/Security/Cryptography/AESUtils.cs
T
2021-02-20 16:45:44 +08:00

595 lines
20 KiB
C#

using System;
using System.Linq;
using System.Text;
namespace System.Security.Cryptography
{
/// <summary>
/// 对称加密算法 - AES
/// </summary>
public static class AESUtils
{
#region 创建 Aes 实例
/// <summary>
/// 创建 <see cref="Aes"/> 实例
/// </summary>
/// <param name="keySize">密钥大小,默认192</param>
/// <param name="padding">填充</param>
/// <param name="mode">模式</param>
/// <returns></returns>
public static Aes Create(int keySize = 192, PaddingMode padding = PaddingMode.PKCS7, CipherMode mode = CipherMode.CBC)
=> new AesCryptoServiceProvider
{
KeySize = keySize,
Padding = padding,
Mode = mode,
};
/// <summary>
/// 通过 <see cref="AesParameters"/>(ByteArray) 创建 <see cref="Aes"/> 实例
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Aes Create(byte[] data) => Parameters.Create(data).Create();
/// <summary>
/// 通过 <see cref="AesParameters"/>(ByteArray) 创建 <see cref="Aes"/> 实例
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Aes? Create_Nullable(byte[]? data)
{
if (data == default) return default;
return Create(data);
}
/// <summary>
/// 通过 <see cref="AesParameters"/>(String) 创建 <see cref="Aes"/> 实例
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Aes Create(string data) => Parameters.Create(data).Create();
/// <summary>
/// 通过 <see cref="AesParameters"/>(String) 创建 <see cref="Aes"/> 实例
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Aes? Create_Nullable(string? data)
{
if (data == default) return default;
return Create(data);
}
#endregion
#region 从 Aes 实例中获取密钥
/// <summary>
/// 将 <see cref="Aes"/> 实例中的 [密钥,向量,模式,填充] 转换为可传输的模型类对象。
/// </summary>
/// <param name="aes"></param>
/// <returns></returns>
internal static Parameters ToParams(this Aes aes) => new Parameters(aes.Key, aes.IV, aes.Mode, aes.Padding);
/// <summary>
/// 将 <see cref="Aes"/> 实例中的密钥转换为可传输的字符串。
/// </summary>
/// <param name="aes"></param>
/// <returns></returns>
public static string ToParamsString(this Aes aes) => aes.ToParams().ToString();
/// <summary>
/// 将 <see cref="Aes"/> 实例中的密钥转换为可传输的 <see cref="byte"/>[] 。
/// </summary>
/// <param name="aes"></param>
/// <returns></returns>
public static byte[] ToParamsByteArray(this Aes aes) => aes.ToParams().ToByteArray();
#endregion
#region 加密(Encrypt)
/// <summary>
/// AES加密(ByteArray → ByteArray)
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] Encrypt(this Aes aes, byte[] data)
{
using var transform = aes.CreateEncryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
/// <summary>
/// AES加密(ByteArray → ByteArray)
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static byte[]? Encrypt_Nullable(this Aes aes, byte[]? data)
{
if (data == default) return default;
return Encrypt(aes, data);
}
/// <summary>
/// AES加密(String → ByteArray)
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></retu
public static byte[] EncryptToByteArray(this Aes aes, string text)
{
var data = Encoding.UTF8.GetBytes(text);
return Encrypt(aes, data);
}
/// <summary>
/// AES加密(String → ByteArray)
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static byte[]? EncryptToByteArray_Nullable(this Aes aes, string? text)
{
if (text == default) return default;
return EncryptToByteArray(aes, text);
}
/// <summary>
/// AES加密(String → String)
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static string Encrypt(this Aes aes, string text)
{
var result = EncryptToByteArray(aes, text);
return result.Base64UrlEncode();
}
/// <summary>
/// AES加密(String → String)
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static string? Encrypt_Nullable(this Aes aes, string? text)
{
if (text == default) return default;
return Encrypt(aes, text);
}
/// <summary>
/// AES加密(ByteArray → String)
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static string EncryptToString(this Aes aes, byte[] data)
{
var result = Encrypt(aes, data);
return result.Base64UrlEncode();
}
/// <summary>
/// AES加密(ByteArray → String)
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static string? EncryptToString_Nullable(this Aes aes, byte[]? data)
{
if (data == default) return default;
return EncryptToString(aes, data);
}
#endregion
#region 解密(Decrypt)
/// <summary>
/// AES解密(ByteArray → ByteArray)
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] Decrypt(this Aes aes, byte[] data)
{
using var transform = aes.CreateDecryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
/// <summary>
/// (解密) ByteArray → ByteArray
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static byte[]? Decrypt_Nullable(this Aes aes, byte[]? data)
{
if (data == default) return default;
return Decrypt(aes, data);
}
/// <summary>
/// (解密) String → ByteArray
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static byte[] DecryptToByteArray(this Aes aes, string text)
{
var data = text.Base64UrlDecodeToByteArray();
return Decrypt(aes, data);
}
/// <summary>
/// (解密) String → ByteArray
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static byte[]? DecryptToByteArray_Nullable(this Aes aes, string? text)
{
if (text == default) return default;
return DecryptToByteArray(aes, text);
}
/// <summary>
/// (解密) String → String
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static string Decrypt(this Aes aes, string text)
{
var result = DecryptToByteArray(aes, text);
return Encoding.UTF8.GetString(result);
}
/// <summary>
/// (解密) String → String
/// </summary>
/// <param name="aes"></param>
/// <param name="text"></param>
/// <returns></returns>
public static string? Decrypt_Nullable(this Aes aes, string? text)
{
if (text == default) return default;
return Decrypt(aes, text);
}
/// <summary>
/// (解密) ByteArray → String
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static string DecryptToString(this Aes aes, byte[] data)
{
var result = Decrypt(aes, data);
return Encoding.UTF8.GetString(result);
}
/// <summary>
/// (解密) ByteArray → String
/// </summary>
/// <param name="aes"></param>
/// <param name="data"></param>
/// <returns></returns>
public static string? DecryptToString_Nullable(this Aes aes, byte[]? data)
{
if (data == default) return default;
return DecryptToString(aes, data);
}
#endregion
/// <summary>
/// <see cref="Aes"/> [密钥,向量,模式,填充] 模型类
/// <para>使用内置函数序列化此模型,不可使用 JSON 或 MessagePack 处理此类</para>
/// <para>String: <see cref="ToString"/> / <see cref="Create(string)"/></para>
/// <para>ByteArray: <see cref="ToByteArray"/> / <see cref="Create(byte[])"/></para>
/// </summary>
internal sealed class Parameters
{
Parameters(CipherMode mode, PaddingMode padding)
{
_mode = mode;
_padding = padding;
OnPropertyChanged();
}
Parameters(Flags flags)
{
OnPropertyChanged(flags);
}
public Parameters(string key, string iv, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) : this(mode, padding)
{
Key = key;
IV = iv;
}
public Parameters(byte[] key, byte[] iv, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) : this(mode, padding)
{
KeyByteArray = key;
IVByteArray = iv;
}
public Parameters(string key, string iv, Flags flags) : this(flags)
{
Key = key;
IV = iv;
}
public Parameters(byte[] key, byte[] iv, Flags flags) : this(flags)
{
KeyByteArray = key;
IVByteArray = iv;
}
[Flags]
public enum Flags
{
CipherMode_CBC = 2,
/// <summary>
/// ECB模式已被定义为不安全的,除非对接第三方服务要求使用之外,否则不应使用!
/// </summary>
[Obsolete("low safety.")] CipherMode_ECB = 16,
//[Obsolete("target .NET Standard 1.4 is not supported.")]
CipherMode_OFB = 32,
//[Obsolete("target .NET Standard 1.4 is not supported.")]
CipherMode_CFB = 64,
CipherMode_CTS = 512,
PaddingMode_None = 4096,
PaddingMode_PKCS7 = 8,
PaddingMode_Zeros = 1024,
//[Obsolete("target .NET Standard 1.4 is not supported.")]
PaddingMode_ANSIX923 = 2048,
//[Obsolete("target .NET Standard 1.4 is not supported.")]
PaddingMode_ISO10126 = 4
}
string? _key;
byte[]? _keyByteArray;
string? _iv;
byte[]? _ivByteArray;
CipherMode _mode;
PaddingMode _padding;
Flags _flags;
/// <summary>
/// 模式与填充
/// </summary>
public Flags ModeAndPadding
{
get => _flags;
set
{
if (value != _flags)
{
OnPropertyChanged(value);
}
}
}
/// <summary>
/// 密钥(String)
/// </summary>
public string? Key
{
get => _key;
set
{
_key = value;
_keyByteArray = value.Base64UrlDecodeToByteArray_Nullable();
}
}
/// <summary>
/// 密钥(ByteArray)
/// </summary>
public byte[]? KeyByteArray
{
get => _keyByteArray;
set
{
_keyByteArray = value;
_key = value.Base64UrlEncode_Nullable();
}
}
/// <summary>
/// 向量(String)
/// </summary>
public string? IV
{
get => _iv;
set
{
_iv = value;
_ivByteArray = value.Base64UrlDecodeToByteArray_Nullable();
}
}
/// <summary>
/// 向量(ByteArray)
/// </summary>
public byte[]? IVByteArray
{
get => _ivByteArray;
set
{
_ivByteArray = value;
_iv = value.Base64UrlEncode_Nullable();
}
}
/// <summary>
/// 模式
/// </summary>
public CipherMode Mode
{
get => _mode;
set
{
if (value != _mode)
{
_mode = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// 填充
/// </summary>
public PaddingMode Padding
{
get => _padding;
set
{
if (value != _padding)
{
_padding = value;
OnPropertyChanged();
}
}
}
void OnPropertyChanged()
{
var mode = Flags.CipherMode_CBC;
var padding = Flags.PaddingMode_PKCS7;
#pragma warning disable CS0618 // 类型或成员已过时
switch (_mode)
{
case CipherMode.ECB:
mode = Flags.CipherMode_ECB;
break;
case CipherMode.OFB:
mode = Flags.CipherMode_OFB;
break;
case CipherMode.CFB:
mode = Flags.CipherMode_CFB;
break;
case CipherMode.CTS:
mode = Flags.CipherMode_CTS;
break;
}
switch (_padding)
{
case PaddingMode.None:
padding = Flags.PaddingMode_None;
break;
case PaddingMode.Zeros:
padding = Flags.PaddingMode_Zeros;
break;
case PaddingMode.ANSIX923:
padding = Flags.PaddingMode_ANSIX923;
break;
case PaddingMode.ISO10126:
padding = Flags.PaddingMode_ISO10126;
break;
}
#pragma warning restore CS0618 // 类型或成员已过时
_flags = mode | padding;
}
void OnPropertyChanged(Flags flags)
{
#pragma warning disable CS0618 // 类型或成员已过时
if ((flags & Flags.CipherMode_CBC) == Flags.CipherMode_CBC)
_mode = CipherMode.CBC;
else if ((flags & Flags.CipherMode_CTS) == Flags.CipherMode_CTS)
_mode = CipherMode.CTS;
else if ((flags & Flags.CipherMode_OFB) == Flags.CipherMode_OFB)
_mode = CipherMode.OFB;
else if ((flags & Flags.CipherMode_CFB) == Flags.CipherMode_CFB)
_mode = CipherMode.CFB;
else if ((flags & Flags.CipherMode_ECB) == Flags.CipherMode_ECB)
_mode = CipherMode.ECB;
if ((flags & Flags.PaddingMode_PKCS7) == Flags.PaddingMode_PKCS7)
_padding = PaddingMode.PKCS7;
else if ((flags & Flags.PaddingMode_Zeros) == Flags.PaddingMode_Zeros)
_padding = PaddingMode.Zeros;
else if ((flags & Flags.PaddingMode_ANSIX923) == Flags.PaddingMode_ANSIX923)
_padding = PaddingMode.ANSIX923;
else if ((flags & Flags.PaddingMode_ISO10126) == Flags.PaddingMode_ISO10126)
_padding = PaddingMode.ISO10126;
else if ((flags & Flags.PaddingMode_None) == Flags.PaddingMode_None)
_padding = PaddingMode.None;
#pragma warning restore CS0618 // 类型或成员已过时
_flags = flags;
}
public static Parameters Create(byte[] data)
{
var uint16 = BitConverter.ToUInt16(data, 0);
var flags = (Flags)uint16;
var IVByteArray = data.Skip(2).Take(16).ToArray();
var KeyByteArray = data.Skip(18).Reverse().ToArray();
return new Parameters(KeyByteArray, IVByteArray, flags);
}
public static Parameters Create(string data)
{
var bytes = data.Base64UrlDecodeToByteArray();
return Create(bytes);
}
public byte[] ToByteArray()
{
if (IVByteArray == null) throw new NullReferenceException(nameof(IVByteArray));
if (KeyByteArray == null) throw new NullReferenceException(nameof(KeyByteArray));
return BitConverter.GetBytes((ushort)ModeAndPadding)
.Concat(IVByteArray)
.Concat(KeyByteArray.Reverse())
.ToArray();
}
public new string ToString() => ToByteArray().Base64UrlEncode();
public Aes Create()
{
if (IVByteArray == null) throw new NullReferenceException(nameof(IVByteArray));
if (KeyByteArray == null) throw new NullReferenceException(nameof(KeyByteArray));
return new AesCryptoServiceProvider
{
Key = KeyByteArray,
IV = IVByteArray,
Mode = Mode,
Padding = Padding
};
}
public bool Equals(Parameters? other)
{
if (other == null) return false;
return Mode == other.Mode &&
Padding == other.Padding &&
KeyByteArray.SequenceEqual_Nullable(other.KeyByteArray) &&
IVByteArray.SequenceEqual_Nullable(other.IVByteArray);
}
}
}
}