93 lines
3.3 KiB
C#
93 lines
3.3 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using UnityEngine;
|
||
|
||
public static class SignUtil
|
||
{
|
||
public static string LastPlainText { get; private set; } = "";
|
||
public static string GetSign(Dictionary<string, string> parameters, string secret)
|
||
{
|
||
// 排序并拼接
|
||
var sorted = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
|
||
sorted.Remove("sign"); // sign 不参与
|
||
StringBuilder sb = new StringBuilder();
|
||
foreach (var kv in sorted)
|
||
sb.Append(kv.Key).Append(kv.Value);
|
||
sb.Append(secret);
|
||
|
||
// MD5 小写
|
||
using var md5 = System.Security.Cryptography.MD5.Create();
|
||
byte[] input = Encoding.UTF8.GetBytes(sb.ToString());
|
||
byte[] hash = md5.ComputeHash(input);
|
||
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||
}
|
||
public static string GetSign(IDictionary<string, object> parameters, string secret)
|
||
{
|
||
// 拷贝并排除 sign
|
||
var dict = parameters
|
||
.Where(kv => !string.Equals(kv.Key, "sign", StringComparison.Ordinal))
|
||
.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||
|
||
// 按 key ASCII 排序(等同 PHP 的 ksort)
|
||
var ordered = dict.OrderBy(k => k.Key, StringComparer.Ordinal);
|
||
|
||
// 拼接 key + value
|
||
var sb = new StringBuilder();
|
||
foreach (var kv in ordered)
|
||
{
|
||
string v = ToFlatString(kv.Value);
|
||
sb.Append(kv.Key).Append(v);
|
||
}
|
||
|
||
// // 追加 AppSecret
|
||
sb.Append(secret);
|
||
|
||
// 记录签名原文,便于排错
|
||
LastPlainText = sb.ToString();
|
||
|
||
|
||
Debug.Log(LastPlainText);
|
||
|
||
|
||
// MD5 小写32位
|
||
using var md5 = MD5.Create();
|
||
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(LastPlainText));
|
||
return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
|
||
}
|
||
|
||
// 将值转成“紧凑字符串”,数组按紧凑 JSON(无空格)序列化
|
||
private static string ToFlatString(object val)
|
||
{
|
||
if (val == null) return "";
|
||
switch (val)
|
||
{
|
||
case string s: return s;
|
||
case bool b: return b ? "true" : "false";
|
||
case Array a: return JsonArrayCompact(a);
|
||
case IList<object> l: return JsonArrayCompact(l.ToArray());
|
||
default:
|
||
if (val is IFormattable f)
|
||
return f.ToString(null, System.Globalization.CultureInfo.InvariantCulture);
|
||
return val.ToString();
|
||
}
|
||
}
|
||
|
||
private static string JsonArrayCompact(Array arr)
|
||
{
|
||
var parts = new List<string>(arr.Length);
|
||
foreach (var x in arr)
|
||
{
|
||
if (x == null) { parts.Add("null"); continue; }
|
||
if (x is string s) parts.Add($"\"{Escape(s)}\"");
|
||
else if (x is bool b) parts.Add(b ? "true" : "false");
|
||
else if (x is IFormattable f) parts.Add(f.ToString(null, System.Globalization.CultureInfo.InvariantCulture));
|
||
else parts.Add($"\"{Escape(x.ToString())}\"");
|
||
}
|
||
return "[" + string.Join(",", parts) + "]";
|
||
}
|
||
|
||
private static string Escape(string s) => s.Replace("\\", "\\\\").Replace("\"", "\\\"");
|
||
} |