|
|
|
|
@ -0,0 +1,306 @@ |
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.IO; |
|
|
|
|
using System.Security.Cryptography; |
|
|
|
|
using System.Text; |
|
|
|
|
|
|
|
|
|
namespace WeiCloud.Utils.EncodeTools |
|
|
|
|
{ |
|
|
|
|
/// <summary> |
|
|
|
|
/// AES加密类 |
|
|
|
|
/// </summary> |
|
|
|
|
public class AESUtils |
|
|
|
|
{ |
|
|
|
|
// AES块大小固定为16字节(128位) |
|
|
|
|
private const int AES_BLOCK_SIZE = 16; |
|
|
|
|
private const string KEY_ALGORITHM = "AES"; |
|
|
|
|
private const string DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; // PKCS7与Java的PKCS5兼容 |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 初始化密钥(128位) |
|
|
|
|
/// </summary> |
|
|
|
|
public static byte[] InitSecretKey() |
|
|
|
|
{ |
|
|
|
|
using (var aes = Aes.Create()) |
|
|
|
|
{ |
|
|
|
|
aes.KeySize = 128; |
|
|
|
|
aes.GenerateKey(); |
|
|
|
|
return aes.Key; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 加密数据 |
|
|
|
|
/// </summary> |
|
|
|
|
public static byte[] Encrypt(byte[] data, byte[] key, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) |
|
|
|
|
{ |
|
|
|
|
// 验证输入参数 |
|
|
|
|
ValidateEncryptionParameters(data, key, cipherAlgorithm); |
|
|
|
|
|
|
|
|
|
var (mode, padding) = ParseAlgorithmParameters(cipherAlgorithm); |
|
|
|
|
|
|
|
|
|
using (var aes = Aes.Create()) |
|
|
|
|
{ |
|
|
|
|
aes.Key = key; |
|
|
|
|
aes.Mode = mode; |
|
|
|
|
aes.Padding = padding; |
|
|
|
|
|
|
|
|
|
// ECB模式不需要IV,其他模式需要生成IV |
|
|
|
|
if (mode != CipherMode.ECB) |
|
|
|
|
{ |
|
|
|
|
aes.GenerateIV(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using (var encryptor = aes.CreateEncryptor()) |
|
|
|
|
{ |
|
|
|
|
// 对于需要IV的模式,将IV前缀到加密结果前 |
|
|
|
|
using (var ms = new MemoryStream()) |
|
|
|
|
{ |
|
|
|
|
// 写入IV(如果不是ECB模式) |
|
|
|
|
if (mode != CipherMode.ECB) |
|
|
|
|
{ |
|
|
|
|
ms.Write(aes.IV, 0, aes.IV.Length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) |
|
|
|
|
{ |
|
|
|
|
cs.Write(data, 0, data.Length); |
|
|
|
|
cs.FlushFinalBlock(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ms.ToArray(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 解密数据 |
|
|
|
|
/// </summary> |
|
|
|
|
public static byte[] Decrypt(byte[] data, byte[] key, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) |
|
|
|
|
{ |
|
|
|
|
// 验证输入参数 |
|
|
|
|
ValidateDecryptionParameters(data, key, cipherAlgorithm); |
|
|
|
|
|
|
|
|
|
var (mode, padding) = ParseAlgorithmParameters(cipherAlgorithm); |
|
|
|
|
byte[] iv = null; |
|
|
|
|
byte[] actualData = data; |
|
|
|
|
|
|
|
|
|
// 对于需要IV的模式,从数据开头提取IV |
|
|
|
|
if (mode != CipherMode.ECB) |
|
|
|
|
{ |
|
|
|
|
if (data.Length < AES_BLOCK_SIZE) |
|
|
|
|
{ |
|
|
|
|
throw new ArgumentException("数据长度不足,无法提取IV"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 提取IV(前16字节) |
|
|
|
|
iv = new byte[AES_BLOCK_SIZE]; |
|
|
|
|
Array.Copy(data, 0, iv, 0, iv.Length); |
|
|
|
|
|
|
|
|
|
// 剩余部分为实际加密数据 |
|
|
|
|
actualData = new byte[data.Length - AES_BLOCK_SIZE]; |
|
|
|
|
Array.Copy(data, AES_BLOCK_SIZE, actualData, 0, actualData.Length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 验证实际数据是否为完整块 |
|
|
|
|
if (actualData.Length % AES_BLOCK_SIZE != 0) |
|
|
|
|
{ |
|
|
|
|
throw new CryptographicException("输入数据不是完整块,请检查数据是否损坏或编码错误"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
using (var aes = Aes.Create()) |
|
|
|
|
{ |
|
|
|
|
aes.Key = key; |
|
|
|
|
aes.Mode = mode; |
|
|
|
|
aes.Padding = padding; |
|
|
|
|
if (iv != null) |
|
|
|
|
{ |
|
|
|
|
aes.IV = iv; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
using (var decryptor = aes.CreateDecryptor()) |
|
|
|
|
{ |
|
|
|
|
using (var ms = new MemoryStream()) |
|
|
|
|
{ |
|
|
|
|
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write)) |
|
|
|
|
{ |
|
|
|
|
cs.Write(actualData, 0, actualData.Length); |
|
|
|
|
cs.FlushFinalBlock(); |
|
|
|
|
} |
|
|
|
|
return ms.ToArray(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (CryptographicException ex) |
|
|
|
|
{ |
|
|
|
|
// 处理填充错误,通常是密钥错误或数据被篡改 |
|
|
|
|
if (ex.Message.Contains("Padding is invalid")) |
|
|
|
|
{ |
|
|
|
|
throw new CryptographicException("解密失败:填充无效。可能原因:密钥错误、数据被篡改或算法参数不匹配", ex); |
|
|
|
|
} |
|
|
|
|
throw; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 字符串加密(Base64输出) |
|
|
|
|
/// </summary> |
|
|
|
|
public static string EncryptString(string plainText, string keyBase64, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
byte[] key = Convert.FromBase64String(keyBase64); |
|
|
|
|
byte[] data = Encoding.UTF8.GetBytes(plainText); |
|
|
|
|
byte[] encrypted = Encrypt(data, key, cipherAlgorithm); |
|
|
|
|
return Convert.ToBase64String(encrypted); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
throw new Exception("字符串加密失败:" + ex.Message, ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 字符串解密(Base64输入) |
|
|
|
|
/// </summary> |
|
|
|
|
public static string DecryptString(string encryptedText, string keyBase64, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
byte[] data = ParseHexStr2Byte(encryptedText); |
|
|
|
|
byte[] key = Convert.FromBase64String(keyBase64); |
|
|
|
|
//byte[] data = Convert.FromBase64String(encryptedText); |
|
|
|
|
byte[] decrypted = Decrypt(data, key, cipherAlgorithm); |
|
|
|
|
return Encoding.UTF8.GetString(decrypted); |
|
|
|
|
} |
|
|
|
|
catch (FormatException) |
|
|
|
|
{ |
|
|
|
|
throw new Exception("解密失败:输入不是有效的Base64字符串"); |
|
|
|
|
} |
|
|
|
|
catch (CryptographicException ex) |
|
|
|
|
{ |
|
|
|
|
throw new Exception("解密失败:" + ex.Message, ex); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
throw new Exception("字符串解密失败:" + ex.Message, ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 验证加密参数 |
|
|
|
|
/// </summary> |
|
|
|
|
private static void ValidateEncryptionParameters(byte[] data, byte[] key, string algorithm) |
|
|
|
|
{ |
|
|
|
|
if (data == null || data.Length == 0) |
|
|
|
|
throw new ArgumentNullException(nameof(data), "待加密数据不能为空"); |
|
|
|
|
|
|
|
|
|
if (key == null || key.Length == 0) |
|
|
|
|
throw new ArgumentNullException(nameof(key), "密钥不能为空"); |
|
|
|
|
|
|
|
|
|
// 验证密钥长度(128/192/256位) |
|
|
|
|
if (key.Length != 16 && key.Length != 24 && key.Length != 32) |
|
|
|
|
throw new ArgumentException("密钥长度必须为16字节(128位)、24字节(192位)或32字节(256位)", nameof(key)); |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
ParseAlgorithmParameters(algorithm); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
throw new ArgumentException("无效的加密算法参数:" + ex.Message, nameof(algorithm), ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 验证解密参数 |
|
|
|
|
/// </summary> |
|
|
|
|
private static void ValidateDecryptionParameters(byte[] data, byte[] key, string algorithm) |
|
|
|
|
{ |
|
|
|
|
if (data == null || data.Length == 0) |
|
|
|
|
throw new ArgumentNullException(nameof(data), "待解密数据不能为空"); |
|
|
|
|
|
|
|
|
|
if (key == null || key.Length == 0) |
|
|
|
|
throw new ArgumentNullException(nameof(key), "密钥不能为空"); |
|
|
|
|
|
|
|
|
|
// 验证密钥长度 |
|
|
|
|
if (key.Length != 16 && key.Length != 24 && key.Length != 32) |
|
|
|
|
throw new ArgumentException("密钥长度必须为16字节(128位)、24字节(192位)或32字节(256位)", nameof(key)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 解析算法参数(模式和填充方式) |
|
|
|
|
/// </summary> |
|
|
|
|
private static (CipherMode mode, PaddingMode padding) ParseAlgorithmParameters(string algorithm) |
|
|
|
|
{ |
|
|
|
|
var parts = algorithm.Split('/'); |
|
|
|
|
if (parts.Length != 3) |
|
|
|
|
throw new ArgumentException("算法参数格式应为:Algorithm/Mode/Padding,例如AES/ECB/PKCS7Padding"); |
|
|
|
|
|
|
|
|
|
CipherMode mode = parts[1].ToUpper() switch |
|
|
|
|
{ |
|
|
|
|
"ECB" => CipherMode.ECB, |
|
|
|
|
"CBC" => CipherMode.CBC, |
|
|
|
|
"CFB" => CipherMode.CFB, |
|
|
|
|
"OFB" => CipherMode.OFB, |
|
|
|
|
"CTS" => CipherMode.CTS, |
|
|
|
|
_ => throw new ArgumentException($"不支持的加密模式:{parts[1]}") |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
PaddingMode padding = parts[2].ToUpper() switch |
|
|
|
|
{ |
|
|
|
|
"PKCS5PADDING" => PaddingMode.PKCS7, |
|
|
|
|
"PKCS7PADDING" => PaddingMode.PKCS7, |
|
|
|
|
"ZEROPADDING" => PaddingMode.Zeros, |
|
|
|
|
"ANSIX923PADDING" => PaddingMode.ANSIX923, |
|
|
|
|
"ISO10126PADDING" => PaddingMode.ISO10126, |
|
|
|
|
"NOPADDING" => PaddingMode.None, |
|
|
|
|
_ => throw new ArgumentException($"不支持的填充方式:{parts[2]}") |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return (mode, padding); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 辅助方法:十六进制与字节数组转换 |
|
|
|
|
public static string BytesToHex(byte[] bytes) |
|
|
|
|
{ |
|
|
|
|
if (bytes == null) return null; |
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
foreach (byte b in bytes) |
|
|
|
|
sb.Append(b.ToString("X2")); |
|
|
|
|
return sb.ToString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static byte[] HexToBytes(string hex) |
|
|
|
|
{ |
|
|
|
|
if (string.IsNullOrEmpty(hex) || hex.Length % 2 != 0) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
byte[] bytes = new byte[hex.Length / 2]; |
|
|
|
|
for (int i = 0; i < hex.Length; i += 2) |
|
|
|
|
{ |
|
|
|
|
if (!byte.TryParse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[i / 2])) |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
return bytes; |
|
|
|
|
} |
|
|
|
|
public static byte[] ParseHexStr2Byte(string hexStr) |
|
|
|
|
{ |
|
|
|
|
if (string.IsNullOrEmpty(hexStr) || hexStr.Length % 2 != 0) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
byte[] result = new byte[hexStr.Length / 2]; |
|
|
|
|
for (int i = 0; i < result.Length; i++) |
|
|
|
|
{ |
|
|
|
|
int high = Convert.ToInt32(hexStr.Substring(i * 2, 1), 16); |
|
|
|
|
int low = Convert.ToInt32(hexStr.Substring(i * 2 + 1, 1), 16); |
|
|
|
|
result[i] = (byte)((high << 4) | low); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |