173 lines
7.3 KiB
C#
173 lines
7.3 KiB
C#
using System.Security.Cryptography;
|
||
using System.Text;
|
||
|
||
namespace PrivateBox.Encryption
|
||
{
|
||
/// <summary>
|
||
/// AES加密解密服务实现(动态盐值+密钥派生)
|
||
/// </summary>
|
||
public class AesEncryptionService:IEncryptionService
|
||
{
|
||
// 配置参数(可通过依赖注入从配置文件读取)
|
||
private const int SaltSize = 16; // 盐值长度(16字节,推荐值)
|
||
private const int KeySize = 16; // 派生密钥长度(16字节 = AES-128)
|
||
private const int Iterations = 10000; // 密钥派生迭代次数(平衡安全性和性能)
|
||
private static readonly HashAlgorithmName HashAlgorithm = HashAlgorithmName.SHA256;
|
||
|
||
public virtual string Encrypt(string plainText,string key) {
|
||
// 验证输入
|
||
if(string.IsNullOrEmpty(plainText))
|
||
throw new ArgumentException("原始字符串不能为空",nameof(plainText));
|
||
if(string.IsNullOrEmpty(key))
|
||
throw new ArgumentException("密钥不能为空",nameof(key));
|
||
|
||
// 1. 随机生成盐值(.NET 7+ 安全随机数生成)
|
||
byte[] salt = RandomNumberGenerator.GetBytes(SaltSize);
|
||
|
||
// 2. 从原始密钥和盐值派生AES密钥
|
||
byte[] derivedKey = DeriveKey(key,salt);
|
||
|
||
// 3. AES加密逻辑
|
||
byte[] encryptedData;
|
||
using(Aes aes = Aes.Create()) {
|
||
aes.Key = derivedKey;
|
||
aes.GenerateIV(); // 生成随机IV(初始化向量)
|
||
aes.Mode = CipherMode.CBC;
|
||
aes.Padding = PaddingMode.PKCS7;
|
||
|
||
// 创建加密器
|
||
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key,aes.IV);
|
||
|
||
using(MemoryStream msEncrypt = new MemoryStream()) {
|
||
// 先写入IV(解密时需要)
|
||
msEncrypt.Write(aes.IV,0,aes.IV.Length);
|
||
|
||
// 写入加密数据
|
||
using(CryptoStream csEncrypt = new CryptoStream(msEncrypt,encryptor,CryptoStreamMode.Write))
|
||
using(StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
|
||
swEncrypt.Write(plainText);
|
||
}
|
||
|
||
encryptedData = msEncrypt.ToArray();
|
||
}
|
||
}
|
||
|
||
// 4. 拼接加密数据和盐值(格式:[加密数据][盐值])
|
||
byte[] result = new byte[encryptedData.Length + SaltSize];
|
||
Array.Copy(encryptedData,0,result,0,encryptedData.Length);
|
||
Array.Copy(salt,0,result,encryptedData.Length,SaltSize);
|
||
var (base64, hash) = GetHash(result);
|
||
return $"{base64}${hash}";
|
||
}
|
||
|
||
public virtual string Decrypt(string cipherText,string key) {
|
||
// 验证输入
|
||
if(string.IsNullOrEmpty(cipherText))
|
||
throw new ArgumentException("加密字符串不能为空",nameof(cipherText));
|
||
if(string.IsNullOrEmpty(key))
|
||
throw new ArgumentException("密钥不能为空",nameof(key));
|
||
|
||
var result = new StringBuilder();
|
||
if(!cipherText.Contains("$")) {
|
||
result.AppendLine("加密值不包含验证");
|
||
}
|
||
if(cipherText.Contains("$")) {
|
||
var sp = cipherText.Split("$");
|
||
cipherText = sp[0];
|
||
if(sp.Length < 2 || string.IsNullOrEmpty(sp[1])) {
|
||
result.AppendLine("存在验证,但是验证码错误!");
|
||
}
|
||
if(!ValidateBase64(sp[0],sp[1])) {
|
||
result.AppendLine("加密验证失败!信息可能被篡改!");
|
||
}
|
||
}
|
||
// 1. 转换Base64为字节数组
|
||
byte[] fullData;
|
||
try {
|
||
fullData = Convert.FromBase64String(cipherText);
|
||
}
|
||
catch(FormatException) {
|
||
throw new ArgumentException("加密字符串不是有效的Base64格式",nameof(cipherText));
|
||
}
|
||
|
||
// 2. 验证数据长度(至少包含IV+盐值)
|
||
if(fullData.Length < 16 + SaltSize) // IV固定16字节 + 盐值长度
|
||
throw new ArgumentException("加密字符串无效(长度不足)",nameof(cipherText));
|
||
|
||
// 3. 提取盐值(从尾部提取)
|
||
byte[] salt = new byte[SaltSize];
|
||
Array.Copy(fullData,fullData.Length - SaltSize,salt,0,SaltSize);
|
||
|
||
// 4. 提取加密数据(排除尾部盐值)
|
||
byte[] encryptedData = new byte[fullData.Length - SaltSize];
|
||
Array.Copy(fullData,0,encryptedData,0,encryptedData.Length);
|
||
|
||
// 5. 从原始密钥和盐值派生AES密钥(与加密时一致)
|
||
byte[] derivedKey = DeriveKey(key,salt);
|
||
|
||
// 6. AES解密逻辑
|
||
using(Aes aes = Aes.Create()) {
|
||
aes.Key = derivedKey;
|
||
aes.Mode = CipherMode.CBC;
|
||
aes.Padding = PaddingMode.PKCS7;
|
||
|
||
// 提取IV(加密数据的前16字节)
|
||
byte[] iv = new byte[16];
|
||
Array.Copy(encryptedData,0,iv,0,iv.Length);
|
||
aes.IV = iv;
|
||
|
||
// 创建解密器
|
||
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key,aes.IV);
|
||
|
||
// 解密数据(排除IV部分)
|
||
using(MemoryStream msDecrypt = new MemoryStream(encryptedData,16,encryptedData.Length - 16))
|
||
using(CryptoStream csDecrypt = new CryptoStream(msDecrypt,decryptor,CryptoStreamMode.Read))
|
||
using(StreamReader srDecrypt = new StreamReader(csDecrypt)) {
|
||
result.AppendLine(srDecrypt.ReadToEnd());
|
||
}
|
||
}
|
||
return result.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基于PBKDF2算法派生密钥
|
||
/// </summary>
|
||
private byte[] DeriveKey(string rawKey,byte[] salt) {
|
||
using(Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(
|
||
rawKey,
|
||
salt,
|
||
Iterations,
|
||
HashAlgorithm)) {
|
||
return deriveBytes.GetBytes(KeySize);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用于hash验证的key,不可以修改,修改会导致验证失败
|
||
/// </summary>
|
||
private readonly string hashKey = "694699FF-7157-4DF2-ACD9-E60CCFCC6007";
|
||
|
||
private (string base64, string hash) GetHash(byte[] bytes) {
|
||
var hk = Encoding.UTF8.GetBytes(hashKey);
|
||
using var sha256 = new HMACSHA256(hk);
|
||
byte[] hmacBytes = sha256.ComputeHash(bytes);
|
||
string hmacStr = BitConverter.ToString(hmacBytes).Replace("-","").ToLower();
|
||
string base64 = Convert.ToBase64String(bytes);
|
||
return (base64, hmacStr);
|
||
}
|
||
|
||
private bool ValidateBase64(string base64String,string hash) {
|
||
try {
|
||
byte[] decodedData = Convert.FromBase64String(base64String);
|
||
var hk = Encoding.UTF8.GetBytes(hashKey);
|
||
using var sha256 = new HMACSHA256(hk);
|
||
byte[] actualHashBytes = sha256.ComputeHash(decodedData);
|
||
string actualHash = BitConverter.ToString(actualHashBytes).Replace("-","").ToLower();
|
||
return actualHash == hash;
|
||
}
|
||
catch(FormatException) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
} |