Просмотр исходного кода

GetStableHashCode、DES 加解密

龙腾猫跃 5 месяцев назад
Родитель
Сommit
19541cecce

+ 21 - 13
Shared/Extensions/StringExtensions.cs

@@ -1,5 +1,4 @@
 using System.Text.RegularExpressions;
-using static System.Net.Mime.MediaTypeNames;
 
 namespace MeloongCore.Extensions;
 public static class StringExtensions {
@@ -129,7 +128,7 @@ public static class StringExtensions {
     public static string BeforeFirst(this string str, string text, bool ignoreCase = false) {
         int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text, ignoreCase);
         if (pos < 0) return str;
-        return str.Substring(0, pos);
+        return str[..pos];
     }
     /// <summary>
     /// 获取在任意子字符串第一次出现之前的部分,如果未找到任意子字符串则不裁切。
@@ -144,7 +143,7 @@ public static class StringExtensions {
             if (p >= 0 && (pos < 0 || pos > p)) pos = p;
         }
         if (pos < 0) return str;
-        return str.Substring(0, pos);
+        return str[..pos];
     }
 
     /// <summary>
@@ -154,7 +153,7 @@ public static class StringExtensions {
     public static string BeforeLast(this string str, string text, bool ignoreCase = false) {
         int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text, ignoreCase);
         if (pos < 0) return str;
-        return str.Substring(0, pos);
+        return str[..pos];
     }
     /// <summary>
     /// 获取在任意子字符串最后一次出现之前的部分,如果未找到任意子字符串则不裁切。
@@ -169,7 +168,7 @@ public static class StringExtensions {
             if (p >= 0 && (pos < 0 || pos < p)) pos = p;
         }
         if (pos < 0) return str;
-        return str.Substring(0, pos);
+        return str[..pos];
     }
 
     /// <summary>
@@ -179,7 +178,7 @@ public static class StringExtensions {
     public static string AfterFirst(this string str, string text, bool ignoreCase = false) {
         int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text, ignoreCase);
         if (pos < 0) return str;
-        return str.Substring(pos + text!.Length);
+        return str[(pos + text!.Length)..];
     }
     /// <summary>
     /// 获取在任意子字符串第一次出现之后的部分,如果未找到任意子字符串则不裁切。
@@ -198,7 +197,7 @@ public static class StringExtensions {
             }
         }
         if (pos < 0) return str;
-        return str.Substring(pos + len);
+        return str[(pos + len)..];
     }
 
     /// <summary>
@@ -208,7 +207,7 @@ public static class StringExtensions {
     public static string AfterLast(this string str, string text, bool ignoreCase = false) {
         int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text, ignoreCase);
         if (pos < 0) return str;
-        return str.Substring(pos + text!.Length);
+        return str[(pos + text!.Length)..];
     }
     /// <summary>
     /// 获取在任意子字符串最后一次出现之后的部分,如果未找到任意子字符串则不裁切。
@@ -227,7 +226,7 @@ public static class StringExtensions {
             }
         }
         if (pos < 0) return str;
-        return str.Substring(pos + len);
+        return str[(pos + len)..];
     }
 
     /// <summary>
@@ -245,9 +244,9 @@ public static class StringExtensions {
 
         int endPos = string.IsNullOrEmpty(before) ? -1 : str.IndexOfF(before, startPos, ignoreCase);
         if (endPos >= 0) {
-            return str.Substring(startPos, endPos - startPos);
+            return str[startPos..endPos];
         } else if (startPos > 0) {
-            return str.Substring(startPos);
+            return str[startPos..];
         } else {
             return str;
         }
@@ -374,7 +373,7 @@ public static class StringExtensions {
     /// </summary>
     public static string Capitalize(this string word) {
         if (string.IsNullOrEmpty(word)) return word;
-        return $"{word.Substring(0, 1).Upper()}{word.Substring(1).Lower()}";
+        return $"{word[..1].Upper()}{word[1..].Lower()}";
     }
 
     /// <summary>
@@ -383,7 +382,7 @@ public static class StringExtensions {
     /// </summary>
     public static string EnsureLength(this string? str, char code, int length) {
         str ??= "";
-        return str.Length > length ? str.Substring(0, length) : str.PadLeft(length, code);
+        return str.Length > length ? str[..length] : str.PadLeft(length, code);
     }
 
     /// <summary>
@@ -392,4 +391,13 @@ public static class StringExtensions {
     public static bool IsAsciiOnly(this string input) 
         => input.All(c => c < 128);
 
+    /// <summary>
+    /// 计算字符串的哈希,其结果在不同环境中均能保持一致。
+    /// </summary>
+    public static ulong GetStableHashCode(this string str) {
+        ulong result = 5381;
+        foreach (char v in str) result = (result << 5) ^ result ^ v;
+        return result ^ 0xA98F501BC684032FUL;
+    }
+
 }

+ 1 - 1
Shared/Main.cs

@@ -2,7 +2,7 @@
 public static class Main {
 
     public static void Init(BaseLogger? logger = null) {
-        AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromSeconds(2));
+        AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromSeconds(5));
         if (logger != null) Logger.Instance = logger;
     }
 

+ 73 - 12
Shared/Utils/CryptographyUtils.cs

@@ -6,7 +6,24 @@ public static class CryptographyUtils {
 
     #region Hash
 
-    public enum HashMethod { Md5, Sha1, Sha256, Sha512 }
+    public enum HashMethod {
+        /// <summary>
+        /// 使用 <see cref="MD5"/> 算法。在 16 进制下哈希为 32 长度字符串。
+        /// </summary>
+        Md5,
+        /// <summary>
+        /// 使用 <see cref="SHA1"/> 算法。在 16 进制下哈希为 40 长度字符串。
+        /// </summary>
+        Sha1,
+        /// <summary>
+        /// 使用 <see cref="SHA256"/> 算法。在 16 进制下哈希为 64 长度字符串。
+        /// </summary>
+        Sha256,
+        /// <summary>
+        /// 使用 <see cref="SHA512"/> 算法。在 16 进制下哈希为 128 长度字符串。
+        /// </summary>
+        Sha512
+    }
     private static HashAlgorithm GetHashAlgorithm(HashMethod method) => method switch {
         HashMethod.Md5 => MD5.Create(),
         HashMethod.Sha1 => SHA1.Create(),
@@ -16,39 +33,83 @@ public static class CryptographyUtils {
     };
 
     /// <summary>
-    /// 计算文件的 Hash。返回十六进制小写字符串。
+    /// 计算文件的 Hash。返回 16 进制小写字符串。
     /// </summary>
     public static string ComputeFileHash(string filePath, HashMethod method) {
         using HashAlgorithm hashImpl = GetHashAlgorithm(method);
         Logger.Trace($"计算文件 {method}:{filePath}");
         using var file = FileUtils.ReadAsStream(filePath);
-        return BitConverter.ToString(hashImpl.ComputeHash(file)).Replace("-", "").ToLower();
+        return BitConverter.ToString(hashImpl.ComputeHash(file)).Replace("-", "").Lower();
     }
 
     /// <summary>
-    /// 计算字节数组的 Hash。返回十六进制小写字符串。
+    /// 计算字节数组的 Hash。返回 16 进制小写字符串。
     /// </summary>
     public static string ComputeHash(byte[] input, HashMethod method) {
         using HashAlgorithm hashImpl = GetHashAlgorithm(method);
-        return BitConverter.ToString(hashImpl.ComputeHash(input)).Replace("-", "").ToLower();
+        return BitConverter.ToString(hashImpl.ComputeHash(input)).Replace("-", "").Lower();
     }
-
     /// <summary>
-    /// 计算字符串的 Hash。返回十六进制小写字符串。
-    /// 使用 UTF-8 将字符串转换为字节数组。
+    /// 计算字符串的 Hash。返回 16 进制小写字符串。
+    /// <para/> 使用 UTF-8 将字符串转换为字节数组。
     /// </summary>
     public static string ComputeHash(string input, HashMethod method) 
         => ComputeHash(Encoding.UTF8.GetBytes(input), method);
 
     #endregion
 
+    #region DES
+    private static readonly byte[] desInitialVector = Encoding.UTF8.GetBytes("95168702");
+
+    /// <summary>
+    /// 未指定密钥时使用的默认 DES 密钥。必须为 8 字节。
+    /// </summary>
+    public static string defaultDesKey = "@;$ Abv2";
+
+    /// <summary>
+    /// 使用 DES 对称加密算法加密字符串。
+    /// </summary>
+    public static string DesEncrypt(string sourceString, string? key = null) {
+        key = key is null ? defaultDesKey : key!.GetStableHashCode().ToString().EnsureLength('X', 8)[..8];
+        byte[] btKey = Encoding.UTF8.GetBytes(key);
+        var des = new DESCryptoServiceProvider();
+        using var ms = new MemoryStream();
+        using var cs = new CryptoStream(ms, des.CreateEncryptor(btKey, desInitialVector), CryptoStreamMode.Write);
+        byte[] inData = Encoding.UTF8.GetBytes(sourceString);
+        cs.Write(inData, 0, inData.Length);
+        cs.FlushFinalBlock();
+        return Convert.ToBase64String(ms.ToArray());
+    }
+
+    /// <summary>
+    /// 使用 DES 对称加密算法解密字符串。
+    /// </summary>
+    public static string DesDecrypt(string encryptedString, string? key = null) {
+        key = key is null ? defaultDesKey : key!.GetStableHashCode().ToString().EnsureLength('X', 8)[..8];
+        byte[] btKey = Encoding.UTF8.GetBytes(key);
+        var des = new DESCryptoServiceProvider();
+        using var ms = new MemoryStream();
+        using var cs = new CryptoStream(ms, des.CreateDecryptor(btKey, desInitialVector), CryptoStreamMode.Write);
+        byte[] inData = Convert.FromBase64String(encryptedString);
+        cs.Write(inData, 0, inData.Length);
+        cs.FlushFinalBlock();
+        return Encoding.UTF8.GetString(ms.ToArray());
+    }
+
+    #endregion
+
     #region ECDSA
 
     /// <summary>
-    /// 进行 ECDSA P-256 签名验证。如果验证失败则抛出 <see cref="CryptographicException"/>。
+    /// 未指定公钥时使用的默认 ECDSA P-256 公钥。
+    /// </summary>
+    public static string defaultEcdsaPublicKey = "RUNTMSAAAAC4QTUNAewh23Q4Q6koHkyIrDIIZUSbua23sf2DiZmIRwSzadISDRyTVTbuWniH3KR7rKj8XBsabms1be6i3c+S";
+
+    /// <summary>
+    /// 进行 ECDSA P-256 非对称加密签名验证。
+    /// 如果验证失败则抛出 <see cref="CryptographicException"/>。
     /// </summary>
-    public static void EcdsaVerify(string sourceString, string sign,
-        string publicKeyBase64 = "RUNTMSAAAAC4QTUNAewh23Q4Q6koHkyIrDIIZUSbua23sf2DiZmIRwSzadISDRyTVTbuWniH3KR7rKj8XBsabms1be6i3c+S") {
+    public static void EcdsaVerify(string sourceString, string sign, string? publicKey = null) {
         using var sha256 = GetHashAlgorithm(HashMethod.Sha256);
         byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(sourceString));
         IntPtr algorithmHandle = IntPtr.Zero;
@@ -57,7 +118,7 @@ public static class CryptographyUtils {
         try {
             int status = BCryptOpenAlgorithmProvider(out algorithmHandle, "ECDSA_P256", null, 0);
             if (status < 0) throw new CryptographicException($"{nameof(BCryptOpenAlgorithmProvider)} 失败,错误码 {status}");
-            status = BCryptImportKeyPair(algorithmHandle, IntPtr.Zero, "ECCPUBLICBLOB", out keyHandle, Convert.FromBase64String(publicKeyBase64), 72, 0);
+            status = BCryptImportKeyPair(algorithmHandle, IntPtr.Zero, "ECCPUBLICBLOB", out keyHandle, Convert.FromBase64String(publicKey ?? defaultEcdsaPublicKey), 72, 0);
             if (status < 0) throw new CryptographicException($"{nameof(BCryptImportKeyPair)} 失败,错误码 {status}");
             status = BCryptVerifySignature(keyHandle, IntPtr.Zero, hash, hash.Length, Convert.FromBase64String(sign), 64, 0);
             if (status == unchecked((int) 0xC000A000)) throw new SecurityException("签名验证失败");

+ 13 - 4
Shared/Utils/MathUtils.cs

@@ -24,13 +24,19 @@ public static class MathUtils {
             if (digit == -1 || digit >= fromRadix) throw new ArgumentException($"Character '{c}' in input '{input}' is not a valid digit for radix {fromRadix}.");
             realNum = realNum * fromRadix + digit;
         }
-        return ConvertRadix(realNum * (isNegative ? -1 : 1), toRadix);
+        return (realNum * (isNegative ? -1 : 1)).ConvertRadix(toRadix);
     }
     /// <summary>
     /// 转换为 2 到 86 进制的字符串。
     /// 进制参考:含大写字母 36,含大小写字母 62,含大小写字母和特殊符号 86。
     /// </summary>
-    public static string ConvertRadix(this long input, int toRadix) 
+    public static string ConvertRadix(this long input, int toRadix)
+        => ConvertRadix((BigInteger) input, toRadix);
+    /// <summary>
+    /// 转换为 2 到 86 进制的字符串。
+    /// 进制参考:含大写字母 36,含大小写字母 62,含大小写字母和特殊符号 86。
+    /// </summary>
+    public static string ConvertRadix(this ulong input, int toRadix)
         => ConvertRadix((BigInteger) input, toRadix);
     /// <summary>
     /// 转换为 2 到 86 进制的字符串。
@@ -44,8 +50,11 @@ public static class MathUtils {
     /// </summary>
     public static string ConvertRadix(this byte input, int toRadix) 
         => ConvertRadix((BigInteger) input, toRadix);
-
-    private static string ConvertRadix(this BigInteger input, int toRadix) {
+    /// <summary>
+    /// 转换为 2 到 86 进制的字符串。
+    /// 进制参考:含大写字母 36,含大小写字母 62,含大小写字母和特殊符号 86。
+    /// </summary>
+    public static string ConvertRadix(this BigInteger input, int toRadix) {
         const string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+=!?@#$%^&*()[]{}<>;:',";
         if (toRadix < 2 || toRadix > digits.Length) throw new ArgumentOutOfRangeException(nameof(toRadix), $"{nameof(toRadix)} must be between 2 and 86.");
         // 零与负数的预处理

+ 1 - 1
Shared/Utils/PathUtils.cs

@@ -63,7 +63,7 @@ public static class PathUtils {
         if (path.StartsWithF(@"\\?\")) return path; // 已经是长路径
         path = RemoveSlashSuffix(path).Replace('/', '\\'); // API 要求这个格式……
         if (path.StartsWithF(@"\\")) {
-            return $@"\\?\UNC\{path.Substring(2)}";
+            return $@"\\?\UNC\{path[2..]}";
         } else {
             return $@"\\?\{path}";
         }