From 672b8c48ac372e6e6413c3242dc3070ee088abb5 Mon Sep 17 00:00:00 2001 From: cryptochecktool Date: Tue, 26 Nov 2024 19:54:46 +0800 Subject: [PATCH] =?UTF-8?q?AesUtils=E9=87=8D=E6=96=B0=E4=BF=AE=E6=94=B9=20?= =?UTF-8?q?(#6165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create AesUtils.cs 新增AesUtils,替换掉DesUtils,提高安全性。 细节: 1.使用AES代替DES 2.正确使用IV调用方式 3.使用SHA256代替MD5进行密钥派生 4.使用PBKDF进行迭代生成密钥。 * Update AesUtils.cs 1.将默认密钥规则设置为与DES一致 2.经过本地测试 --- v2rayN/ServiceLib/Common/AesUtils.cs | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 v2rayN/ServiceLib/Common/AesUtils.cs diff --git a/v2rayN/ServiceLib/Common/AesUtils.cs b/v2rayN/ServiceLib/Common/AesUtils.cs new file mode 100644 index 00000000..f6ccf761 --- /dev/null +++ b/v2rayN/ServiceLib/Common/AesUtils.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace ServiceLib.Common +{ + public class AesUtils + { + private const int KeySize = 256; // AES-256 + private const int IvSize = 16; // AES block size + private const int Iterations = 10000; + + private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值 + + private static readonly string DefaultPassword =Utils.GetHomePath() + "AesUtils"; + + /// + /// Encrypt + /// + /// Plain text + /// Password for key derivation or direct key in ASCII bytes + /// Base64 encoded cipher text with IV + public static string Encrypt(string text, string password = null) + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + byte[] plaintext = Encoding.UTF8.GetBytes(text); + byte[] key = GetKey(password); + byte[] iv = GenerateIv(); + + using (Aes aes = Aes.Create()) + { + aes.Key = key; + aes.IV = iv; + + using (MemoryStream ms = new MemoryStream()) + { + ms.Write(iv, 0, iv.Length); + + using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(plaintext, 0, plaintext.Length); + cs.FlushFinalBlock(); + } + + byte[] cipherTextWithIv = ms.ToArray(); + return Convert.ToBase64String(cipherTextWithIv); + } + } + } + + /// + /// Decrypt + /// + /// Base64 encoded cipher text with IV + /// Password for key derivation or direct key in ASCII bytes + /// Plain text + public static string Decrypt(string cipherTextWithIv, string password = null) + { + if (string.IsNullOrEmpty(cipherTextWithIv)) + return string.Empty; + + byte[] cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv); + byte[] key = GetKey(password); + + byte[] iv = new byte[IvSize]; + Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize); + + byte[] cipherText = new byte[cipherTextWithIvBytes.Length - IvSize]; + Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length); + + using (Aes aes = Aes.Create()) + { + aes.Key = key; + aes.IV = iv; + + using (MemoryStream ms = new MemoryStream()) + { + using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(cipherText, 0, cipherText.Length); + cs.FlushFinalBlock(); + } + + byte[] plainText = ms.ToArray(); + return Encoding.UTF8.GetString(plainText); + } + } + } + + private static byte[] GetKey(string password) + { + if (string.IsNullOrEmpty(password)) + { + return GetDefaultKey(); + } + else + { + byte[] key = Encoding.ASCII.GetBytes(password); + if (key.Length != KeySize / 8) + { + throw new ArgumentException($"Password bytes length must be {KeySize / 8} bytes."); + } + return key; + } + } + + private static byte[] GetDefaultKey() + { + using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(DefaultPassword, Salt, Iterations, HashAlgorithmName.SHA256)) + { + return pbkdf2.GetBytes(KeySize / 8); + } + } + + private static byte[] GenerateIv() + { + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + byte[] iv = new byte[IvSize]; + rng.GetBytes(iv); + return iv; + } + } + } +}