using Newtonsoft.Json.Linq; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_Core.Helper; using Newtonsoft.Json.Serialization; namespace WIDESEA_External.PLSService { public class MD5Util { // 获得32位的MD5加密 public static string GetMD5_32(string input) { System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); StringBuilder sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } // 获得16位的MD5加密 public static string GetMD5_16(string input) { return GetMD5_32(input).Substring(8, 16); } // 获得8位的MD5加密 public static string GetMD5_8(string input) { return GetMD5_32(input).Substring(8, 8); } // 获得4位的MD5加密 public static string GetMD5_4(string input) { return GetMD5_32(input).Substring(8, 4); } // 添加MD5的前缀,便于检查有无篡改 public static string AddMD5Profix(string input) { return GetMD5_4(input) + input; } // 移除MD5的前缀 public static string RemoveMD5Profix(string input) { return input.Substring(4); } // 验证MD5前缀处理的字符串有无被篡改 public static bool ValidateValue(string input) { bool res = false; if (input.Length >= 4) { string tmp = input.Substring(4); if (input.Substring(0, 4) == GetMD5_4(tmp)) { res = true; } } return res; } /// /// 对json得键进行排序 /// /// /// public static string SortJson(string json) { var tokenType = GetJsonType(json); if (tokenType == JTokenType.Object) { var dic = JsonConvert.DeserializeObject>(json); SortedDictionary keyValues = new SortedDictionary(dic); keyValues.OrderBy(m => m.Key);//升序 把Key换成Value 就是对Value进行排序 //keyValues.OrderByDescending(m => m.Key);//降序 SortedDictionary tempKeyValues = new SortedDictionary(keyValues); foreach (KeyValuePair kv in tempKeyValues) { // 判断value是不是JObject类型 Type t1 = kv.Value.GetType(); if (t1 == typeof(JObject)) { // value是JObject类型 string jsonItem = JsonConvert.SerializeObject(kv.Value); jsonItem = SortJson(jsonItem); keyValues[kv.Key] = JsonConvert.DeserializeObject(jsonItem); } else if (t1 == typeof(JArray)) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("["); bool isFirst = true; foreach (JToken item in (JArray)kv.Value) { if (item.Type == JTokenType.Object) { string jsonArrayItem = JsonConvert.SerializeObject(item); jsonArrayItem = SortJson(jsonArrayItem); if (isFirst) { stringBuilder.Append(jsonArrayItem); } else { stringBuilder.Append($",{jsonArrayItem}"); } } else if (item.Type == JTokenType.Array) { var arrayTmpRes = SortJson(JsonConvert.SerializeObject(item)); if (isFirst) { stringBuilder.Append(arrayTmpRes); } else { stringBuilder.Append($",{arrayTmpRes}"); } } else { string jsonArrayItem = JsonConvert.SerializeObject(item); if (isFirst) { stringBuilder.Append(jsonArrayItem); } else { stringBuilder.Append($",{jsonArrayItem}"); } } isFirst = false; } stringBuilder.Append("]"); var tmpJsonStr = stringBuilder.ToString(); keyValues[kv.Key] = JsonConvert.DeserializeObject(tmpJsonStr); } } return JsonConvert.SerializeObject(keyValues); } else if (tokenType == JTokenType.Array) { var arraies = JsonConvert.DeserializeObject(json); StringBuilder stringBuilder2 = new StringBuilder(); stringBuilder2.Append("["); bool isFirst2 = true; foreach (JToken item in arraies) { if (item.Type == JTokenType.Object) { string jsonArrayItem = JsonConvert.SerializeObject(item); jsonArrayItem = SortJson(jsonArrayItem); if (isFirst2) { stringBuilder2.Append(jsonArrayItem); } else { stringBuilder2.Append($",{jsonArrayItem}"); } } else if (item.Type == JTokenType.Array) { var arrayTmpRes = SortJson(JsonConvert.SerializeObject(item)); if (isFirst2) { stringBuilder2.Append(arrayTmpRes); } else { stringBuilder2.Append($",{arrayTmpRes}"); } } else { string jsonArrayItem = JsonConvert.SerializeObject(item); if (isFirst2) { stringBuilder2.Append(jsonArrayItem); } else { stringBuilder2.Append($",{jsonArrayItem}"); } } isFirst2 = false; } stringBuilder2.Append("]"); return stringBuilder2.ToString(); } return json; } /// /// 根据json判断json类型 /// /// /// public static JTokenType GetJsonType(string json) { JToken token = JToken.Parse(json); return token.Type; } #region MD5签名验证 /// /// 对给定文件路径的文件加上标签 /// /// 要加密的文件的路径 /// 标签的值 public static bool AddMD5(string path) { bool IsNeed = true; //已进行MD5处理 if (CheckMD5(path)) IsNeed = false; try { FileStream fsread = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] md5File = new byte[fsread.Length]; // 将文件流读取到Buffer中 fsread.Read(md5File, 0, (int)fsread.Length); fsread.Close(); if (IsNeed) { // 对Buffer中的字节内容算MD5 string result = MD5Buffer(md5File, 0, md5File.Length); // 将字符串转换成字节数组以便写人到文件中 byte[] md5 = System.Text.Encoding.ASCII.GetBytes(result); FileStream fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); // 将文件,MD5值 重新写入到文件中。 fsWrite.Write(md5File, 0, md5File.Length); fsWrite.Write(md5, 0, md5.Length); fsWrite.Close(); } else { FileStream fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); fsWrite.Write(md5File, 0, md5File.Length); fsWrite.Close(); } } catch { return false; } return true; } /// /// 对给定路径的文件进行验证 /// /// /// 是否加了标签或是否标签值与内容值一致 public static bool CheckMD5(string path) { try { FileStream get_file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // 读入文件 byte[] md5File = new byte[get_file.Length]; get_file.Read(md5File, 0, (int)get_file.Length); get_file.Close(); // 对文件除最后32位以外的字节计算MD5,这个32是因为标签位为32位。 string result = MD5Buffer(md5File, 0, md5File.Length - 32); //读取文件最后32位,其中保存的就是MD5值 string md5 = Encoding.ASCII.GetString(md5File, md5File.Length - 32, 32); return result == md5; } catch { return false; } } /// /// 计算文件的MD5值 /// /// MD5签名文件字符数组 /// 计算起始位置 /// 计算终止位置 /// 计算结果 private static string MD5Buffer(byte[] MD5File, int index, int count) { System.Security.Cryptography.MD5CryptoServiceProvider get_md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] hash_byte = get_md5.ComputeHash(MD5File, index, count); string result = BitConverter.ToString(hash_byte); result = result.Replace("-", ""); return result; } #endregion //自定义序列化工具类 public class JsonPropertySortResolver : Newtonsoft.Json.Serialization.DefaultContractResolver { protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList(); } } public class SortedCamelCaseContractResolver : DefaultContractResolver { protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { // 获取所有需要序列化的属性(基类方法已过滤忽略属性等) var properties = base.CreateProperties(type, memberSerialization).ToList(); // 先对每个属性应用 CamelCase 命名(确保最终输出的字段名是小驼峰) foreach (var prop in properties) { prop.PropertyName = GetCamelCaseName(prop.UnderlyingName); } // 按属性名(即小驼峰后的名称)字母升序排序 return properties.OrderBy(p => p.PropertyName).ToList(); } private string GetCamelCaseName(string name) { if (string.IsNullOrEmpty(name) || char.IsLower(name[0])) return name; return char.ToLower(name[0]) + name.Substring(1); } } //入参加密前置格式化 public static string GetParamStr(T objectParam) { // 获取JSON串,但排除sign字段 Func filter = (object obj, string field, object value) => !field.Equals("sign"); string paramStr = JsonConvert.SerializeObject(objectParam, new JsonSerializerSettings { ContractResolver = new SortedCamelCaseContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DateFormatString = "yyyy-MM-dd HH:mm:ss", DateTimeZoneHandling = DateTimeZoneHandling.Unspecified, Converters = new List { new DecimalWithoutTrailingZerosConverter() } }); //排序JOSN,得到格式化的串 return SortJson(paramStr); } public class DecimalWithoutTrailingZerosConverter : JsonConverter { public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer) { // 判断是否为整数(尾数为零) if (value == Math.Floor(value)) { writer.WriteValue(Convert.ToInt64(value)); } else { writer.WriteValue(value); } } public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer) { // 反序列化时直接转换为 decimal,兼容整数和小数 return Convert.ToDecimal(reader.Value); } } /// /// 通用PLS接口签名校验 /// public static bool CheckPLSSign(T model) where T : class { try { // 固定配置 string localAppId = AppSettings.Get("LocalAppId"); string localAppSecret = AppSettings.Get("LocalAppSecret"); var type = typeof(T); object signModel = null; // ========== 自动获取公共字段 ========== var timeStampProp = type.GetProperty("TimeStamp"); var signProp = type.GetProperty("Sign"); var dataProp = type.GetProperty("Data"); if (timeStampProp == null || signProp == null || dataProp == null) return false; // 获取值 string requestSign = signProp.GetValue(model)?.ToString() ?? ""; object timeStampVal = timeStampProp.GetValue(model); object dataVal = dataProp.GetValue(model); //时间戳校验 long nowTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long timeStamp = 0; if (timeStampVal is long lng) timeStamp = lng; else if (timeStampVal is string str && long.TryParse(str, out long t)) timeStamp = t; //构建签名对象 if (localAppId != null) { signModel = new { AppId = localAppId, TimeStamp = timeStamp, Data = dataVal }; } else { return false; } string paramStr = GetParamStr(signModel); string localSign = GetMD5_32(paramStr + localAppSecret); ///签名比对 return localSign.Equals(requestSign, StringComparison.OrdinalIgnoreCase); } catch { return false; } } } }