using Google.Protobuf.WellKnownTypes; using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace Sog { /// /// Hash辅助类 /// public class HashHelper { //注意:这个算法不允许修改!!,改了会导致运营的项目的悲剧 // 自己实现字符串的hash算法,不用C#缺省的string.HashCode(),方便其他程序和工具计算结果一致 //这个算法来自Brian Kernighan 和 Dennis Ritchie的 The C Programming Language。 //这是一个很简单的哈希算法,使用了一系列奇怪的数字,形式如31,3131,31...31,看上去和DJB算法很相似。(这个就是Java的字符串哈希函数) //c#内部用的应该和这个类似 public static int HashStringForIndex(string str) { const int seed = 131; int hash = 0; for (int i = 0; i < str.Length; i++) { hash = hash * seed + str[i]; } //保证正数 return hash & 0x7FFFFFFF; } public static string MD5Bytes(byte[] data) { return HashBytes(data, "md5"); } public static string MD5Utf8String(string str) { var md5 = MD5.Create(); byte[] strbyte = Encoding.UTF8.GetBytes(str); byte[] md5Hash = md5.ComputeHash(strbyte); string md5Str = BitConverter.ToString(md5Hash).Replace("-", "").ToLower(); return md5Str; } /// /// 计算文件的 MD5 值 /// /// 要计算 MD5 值的文件名和路径 /// MD5 值16进制字符串 public static string MD5String(string str) { return HashString(str, "md5"); } /// /// 计算文件的 sha1 值 /// /// string /// sha1 值16进制字符串 public static string SHA1String(string str) { return HashString(str, "sha1"); } /// /// 计算文件的 MD5 值 /// /// 要计算 MD5 值的文件名和路径 /// MD5 值16进制字符串 public static string MD5File(string fileName) { return HashFile(fileName, "md5"); } /// /// 计算文件的 sha1 值 /// /// 要计算 sha1 值的文件名和路径 /// sha1 值16进制字符串 public static string SHA1File(string fileName) { return HashFile(fileName, "sha1"); } /// /// 计算文件的哈希值 /// /// string /// 算法:sha1,md5 /// 哈希值16进制字符串 private static string HashString(string str, string algName) { if (str == null || algName == null) { return string.Empty; } byte[] array = Encoding.UTF8.GetBytes(str); using (MemoryStream stream = new MemoryStream(array)) { byte[] hashBytes = HashData(stream, algName); if (hashBytes != null) { return ByteArrayToHexString(hashBytes); } } return string.Empty; } /// /// 计算文件的哈希值 /// /// 要计算哈希值的文件名和路径 /// 算法:sha1,md5 /// 哈希值16进制字符串 private static string HashFile(string fileName, string algName) { if (! File.Exists(fileName)) { return string.Empty; } FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); byte[] hashBytes = HashData(fs, algName); fs.Close(); if (hashBytes != null) { return ByteArrayToHexString(hashBytes); } return string.Empty; } private static HashAlgorithm CreateHashAlgorithm(string algName) { HashAlgorithm algorithm = null; if (string.Compare(algName, "sha1", true) == 0) { algorithm = SHA1.Create(); } else if (string.Compare(algName, "md5", true) == 0) { algorithm = MD5.Create(); } return algorithm; } /// /// 计算哈希值 /// /// 要计算哈希值的 Stream /// 算法:sha1,md5 /// 哈希值字节数组 private static byte[] HashData(Stream stream, string algName) { if (algName == null) { return null; } var algorithm = CreateHashAlgorithm(algName); if (algorithm == null) { return null; } try { return algorithm.ComputeHash(stream); } catch { algorithm.Dispose(); return null; } } private static string HashBytes(byte[] data, string algName) { if (data == null || algName == null) { return string.Empty; } var algorithm = CreateHashAlgorithm(algName); if (algorithm == null) { return string.Empty; } try { byte[] hashData = algorithm.ComputeHash(data); return ByteArrayToHexString(hashData); } catch { algorithm.Dispose(); } return string.Empty; } /// /// 字节数组转换为16进制表示的字符串 /// private static string ByteArrayToHexString(byte[] buf) { // stackoverflow有帖子提到BitConverter有的版本会用"-"把bytes分组 return BitConverter.ToString(buf).Replace("-", "").ToLower(); } /// /// 获取HMACSHA256 加密字符串 16位编码 /// /// /// /// public static string GetHMACSHA256(string key, string mesg) { StringBuilder hex = new StringBuilder(); // 使用UTF-8编码将字符串转换为字节数组 byte[] keyByte = Encoding.UTF8.GetBytes(key); byte[] messageBytes = Encoding.UTF8.GetBytes(mesg); // 创建一个新的HMACSHA256实例,使用appKey作为密钥 using (HMACSHA256 hmac = new HMACSHA256(keyByte)) { byte[] hashMessage = hmac.ComputeHash(messageBytes); foreach (byte b in hashMessage) { hex.AppendFormat("{0:x2}", b); } } return hex.ToString(); } } }