WebAPI 使用者認證防篡改實現HMAC(一)MD5簽名獲取
阿新 • • 發佈:2019-01-31
在開始前先說下HMAC防篡改機制的原理,如果已經接觸過支付寶的可以跳過此部分
防篡改,顧名思義就是防止有人惡意篡改請求資料以達到惡意攻擊的目的,那要怎麼才能實現這樣的目的呢?其實很簡單,將要請求的資料加上合作號、合作Key按規則組織成一個字串,獲取對應的MD5摘要,然後將該摘要及合作號同時作為請求的一部分一起傳遞(合作Key禁止傳遞)
下面進行舉例:
假定需要進行簽名的引數如下(以json格式舉例):
{‘partner’: ‘3122131212’,‘orderNo’:‘1234567’}
對數組裡的每一個鍵按預設的字母正向排序,最後加上partner對應的key進行MD5加密,假定對應的key為bbb,則需要進行MD5摘要的字串如下:
partner=3122131212&orderNo=1234567bbb
最終需要傳遞的請求資料格式如下(分別列舉GET和POST方式,服務實際支援哪種方式以服務宣告為準):
GET:partner=3122131212&orderNo=1234567&sign=EBFE84D02E8E40952899EE5CDFE5404C
POST
:
{‘partner’:‘3122131212’,‘orderNo’:‘1234567’ ,‘sign:‘EBFE84D02E8E40952899EE5CDFE5404C
’}
上例中partner為平臺提供的合作號,key為合作密碼,sign為簽名,然後此處例子是將Get和Post區分開來的,實際獲取MD5的程式碼會支援QueryString及Form同時存在的情況,且當兩者同時存在時,組織字串順序為先QueryString後Form
以下是簽名摘要生成程式碼
使用例子using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Cryptography; using System.Text; public static class SecuritySignHelper { public const string Partner = "partner"; public const string Sign = "sign"; /// <summary> /// 獲取防篡改簽名,組織原始字串的方式為:先get後post,該簽名要求partner在加密時為全小寫,同時該方法隱含要求parnter和sign必須通過QueryString方式傳遞 /// </summary> /// <param name="getCollection">通過QueryString方式傳遞的鍵值集合,如果內部包含parnter或者sign,相關欄位在組織原始字串時將會被移除</param> /// <param name="partner">合作賬號</param> /// <param name="partnerKey">合作Key</param> /// <param name="postCollection">通過Form方式傳遞的鍵值集合,如果包含parnter或者sign,此部分不會被做特殊處理</param> /// <returns></returns> public static string GetSecuritySign(this NameValueCollection getCollection, string partner, string partnerKey, NameValueCollection postCollection = null) { if (string.IsNullOrWhiteSpace(partner) || string.IsNullOrWhiteSpace(partnerKey)) { throw new ArgumentNullException(); } var dic = SecuritySignHelper.GetSortedDictionary(getCollection, (k) => {//過濾partner及sign return string.Equals(k, SecuritySignHelper.Partner, StringComparison.OrdinalIgnoreCase) || string.Equals(k, SecuritySignHelper.Sign, StringComparison.OrdinalIgnoreCase); }); dic.Add(SecuritySignHelper.Partner, partner); StringBuilder tmp = new StringBuilder(); SecuritySignHelper.FillStringBuilder(tmp, dic);//將QueryString填入StringBuilder dic = SecuritySignHelper.GetSortedDictionary(postCollection); SecuritySignHelper.FillStringBuilder(tmp, dic);//將Form填入StringBuilder tmp.Append(partnerKey);//在尾部新增key tmp.Remove(0, 1);//移除第一個& return tmp.ToString().GetMD5_32();//獲取32位長度的Md5摘要 } private static SortedDictionary<string, string> GetSortedDictionary(NameValueCollection collection, Func<string, bool> filter = null) {//獲取排序的鍵值對 SortedDictionary<string, string> dic = new SortedDictionary<string, string>(); if (collection != null && collection.Count > 0) { foreach (var k in collection.AllKeys) { if (filter == null || !filter(k)) {//如果沒設定過濾條件或者無需過濾 dic.Add(k, collection[k]); } } } return dic; } private static void FillStringBuilder(StringBuilder builder, SortedDictionary<string, string> dic) { foreach (var kv in dic) { builder.Append('&'); builder.Append(kv.Key); builder.Append('='); builder.Append(kv.Value); }//按key順序組織字串 } /// <summary> /// 獲取32位長度的Md5摘要 /// </summary> /// <param name="inputStr"></param> /// <param name="encoding"></param> /// <returns></returns> public static string GetMD5_32(this string inputStr, Encoding encoding = null) { RefEncoding(ref encoding); byte[] data = GetMD5(inputStr, encoding); StringBuilder tmp = new StringBuilder(); for (int i = 0; i < data.Length; i++) { tmp.Append(data[i].ToString("x2")); } return tmp.ToString(); } private static byte[] GetMD5(string inputStr, Encoding encoding) { using (MD5 md5Hash = MD5.Create()) { return md5Hash.ComputeHash(encoding.GetBytes(inputStr)); } } private static void RefEncoding(ref Encoding encoding) { if (encoding == null) { encoding = Encoding.Default; } } }
string partner = "zhangsan";
string partnerKey = "bbb";
NameValueCollection getCollection = new NameValueCollection();
string md50 = getCollection.GetSecuritySign(partner, partnerKey);//不帶任何引數的請求
getCollection.Add("test", "123");
string md51 = getCollection.GetSecuritySign(partner, partnerKey);//帶一個引數的請求
NameValueCollection postCollection = new NameValueCollection();
postCollection.Add("Name", "張三");
postCollection.Add("Address", "上海");
string md52 = getCollection.GetSecuritySign(partner, partnerKey, postCollection);//同時存在QueryString及Form的請求