更新加密方式
This commit is contained in:
		
							parent
							
								
									0963b55fe9
								
							
						
					
					
						commit
						a3457332df
					
				
							
								
								
									
										173
									
								
								PrivateBox/Encryption/AesEncryptionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								PrivateBox/Encryption/AesEncryptionService.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
				
			|||||||
 | 
					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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								PrivateBox/Encryption/AesEncryptionServiceV1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								PrivateBox/Encryption/AesEncryptionServiceV1.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace PrivateBox.Encryption
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// v1版本加密服务,支持增加版本,json格式存放
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public class AesEncryptionServiceV1:AesEncryptionService
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 加密版本
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        protected virtual string Ver => "v1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override string Encrypt(string plainText,string key) {
 | 
				
			||||||
 | 
					            var b = base.Encrypt(plainText,key);
 | 
				
			||||||
 | 
					            var sp = b.Split("$",2);
 | 
				
			||||||
 | 
					            var model = new EncryptionModel {
 | 
				
			||||||
 | 
					                Ver = Ver,
 | 
				
			||||||
 | 
					                Base64 = sp[0],
 | 
				
			||||||
 | 
					                Hash = sp[1],
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            return JsonSerializer.Serialize(model);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override string Decrypt(string cipherText,string key) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                var modelStr = JsonSerializer.Deserialize<EncryptionModel>(cipherText);
 | 
				
			||||||
 | 
					                if(modelStr == null) {
 | 
				
			||||||
 | 
					                    throw new Exception("json解码失败!");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                cipherText = $"{modelStr.Base64}${modelStr.Hash}";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch(Exception) {
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return base.Decrypt(cipherText,key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								PrivateBox/Encryption/EncryptionModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								PrivateBox/Encryption/EncryptionModel.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					namespace PrivateBox.Encryption
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 加密存储模型
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public class EncryptionModel
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 加密存储版本
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string? Ver { get; set; }
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 加密后base编码
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string? Base64 { get; set; }
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 哈希值
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string? Hash { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								PrivateBox/Encryption/IEncryptionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								PrivateBox/Encryption/IEncryptionService.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					namespace PrivateBox.Encryption
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 加密解密服务接口
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public interface IEncryptionService
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 加密字符串
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="plainText">原始字符串</param>
 | 
				
			||||||
 | 
					        /// <param name="key">加密密钥(任意长度)</param>
 | 
				
			||||||
 | 
					        /// <returns>加密后的Base64字符串(包含盐值,格式:[加密数据][盐值])</returns>
 | 
				
			||||||
 | 
					        string Encrypt(string plainText,string key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 解密字符串
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="cipherText">加密的Base64字符串(包含盐值)</param>
 | 
				
			||||||
 | 
					        /// <param name="key">解密密钥(与加密密钥相同)</param>
 | 
				
			||||||
 | 
					        /// <returns>解密后的原始字符串</returns>
 | 
				
			||||||
 | 
					        string Decrypt(string cipherText,string key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,193 +0,0 @@
 | 
				
			|||||||
using System.Security.Cryptography;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// <summary>
 | 
					 | 
				
			||||||
/// 加密解密服务接口
 | 
					 | 
				
			||||||
/// </summary>
 | 
					 | 
				
			||||||
public interface IEncryptionService
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 加密字符串
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="plainText">原始字符串</param>
 | 
					 | 
				
			||||||
    /// <param name="key">加密密钥(任意长度)</param>
 | 
					 | 
				
			||||||
    /// <returns>加密后的Base64字符串(包含盐值,格式:[加密数据][盐值])</returns>
 | 
					 | 
				
			||||||
    string Encrypt(string plainText,string key);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 解密字符串
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="cipherText">加密的Base64字符串(包含盐值)</param>
 | 
					 | 
				
			||||||
    /// <param name="key">解密密钥(与加密密钥相同)</param>
 | 
					 | 
				
			||||||
    /// <returns>解密后的原始字符串</returns>
 | 
					 | 
				
			||||||
    string Decrypt(string cipherText,string key);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// <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 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 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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using PrivateBox.DataContext;
 | 
					using PrivateBox.DataContext;
 | 
				
			||||||
 | 
					using PrivateBox.Encryption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace PrivateBox
 | 
					namespace PrivateBox
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using PrivateBox.Encryption;
 | 
				
			||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace PrivateBox
 | 
					namespace PrivateBox
 | 
				
			||||||
@ -30,7 +31,7 @@ namespace PrivateBox
 | 
				
			|||||||
                    services.AddSingleton(t);
 | 
					                    services.AddSingleton(t);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            services.AddSingleton<IEncryptionService,AesEncryptionService>();
 | 
					            services.AddSingleton<IEncryptionService,AesEncryptionServiceV1>();
 | 
				
			||||||
            services.AddSingleton<DbContext>();
 | 
					            services.AddSingleton<DbContext>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user