WebAPI 用户认证防篡改实现HMAC(一)MD5签名获取

来源:互联网 发布:淘宝发布宝贝不同价格 编辑:程序博客网 时间:2024/05/17 21:39

在开始前先说下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

以下是签名摘要生成代码

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Collections.Specialized;  
  4. using System.Security.Cryptography;  
  5. using System.Text;  
  6.   
  7. public static class SecuritySignHelper  
  8. {  
  9.     public const string Partner = "partner";  
  10.     public const string Sign = "sign";  
  11.     /// <summary>  
  12.     /// 获取防篡改签名,组织原始字符串的方式为:先get后post,该签名要求partner在加密时为全小写,同时该方法隐含要求parnter和sign必须通过QueryString方式传递  
  13.     /// </summary>  
  14.     /// <param name="getCollection">通过QueryString方式传递的键值集合,如果内部包含parnter或者sign,相关字段在组织原始字符串时将会被移除</param>  
  15.     /// <param name="partner">合作账号</param>  
  16.     /// <param name="partnerKey">合作Key</param>  
  17.     /// <param name="postCollection">通过Form方式传递的键值集合,如果包含parnter或者sign,此部分不会被做特殊处理</param>  
  18.     /// <returns></returns>  
  19.     public static string GetSecuritySign(this NameValueCollection getCollection, string partner, string partnerKey, NameValueCollection postCollection = null)  
  20.     {  
  21.         if (string.IsNullOrWhiteSpace(partner) || string.IsNullOrWhiteSpace(partnerKey))  
  22.         {  
  23.             throw new ArgumentNullException();  
  24.         }  
  25.         var dic = SecuritySignHelper.GetSortedDictionary(getCollection,  
  26.             (k) =>  
  27.             {//过滤partner及sign  
  28.                 return string.Equals(k, SecuritySignHelper.Partner, StringComparison.OrdinalIgnoreCase)  
  29.                     || string.Equals(k, SecuritySignHelper.Sign, StringComparison.OrdinalIgnoreCase);  
  30.             });  
  31.         dic.Add(SecuritySignHelper.Partner, partner);  
  32.         StringBuilder tmp = new StringBuilder();  
  33.         SecuritySignHelper.FillStringBuilder(tmp, dic);//将QueryString填入StringBuilder  
  34.         dic = SecuritySignHelper.GetSortedDictionary(postCollection);  
  35.         SecuritySignHelper.FillStringBuilder(tmp, dic);//将Form填入StringBuilder  
  36.         tmp.Append(partnerKey);//在尾部添加key  
  37.         tmp.Remove(0, 1);//移除第一个&  
  38.         return tmp.ToString().GetMD5_32();//获取32位长度的Md5摘要  
  39.     }  
  40.     private static SortedDictionary<stringstring> GetSortedDictionary(NameValueCollection collection, Func<stringbool> filter = null)  
  41.     {//获取排序的键值对  
  42.         SortedDictionary<stringstring> dic = new SortedDictionary<stringstring>();  
  43.         if (collection != null && collection.Count > 0)  
  44.         {  
  45.             foreach (var k in collection.AllKeys)  
  46.             {  
  47.                 if (filter == null || !filter(k))  
  48.                 {//如果没设置过滤条件或者无需过滤  
  49.                     dic.Add(k, collection[k]);  
  50.                 }  
  51.             }  
  52.         }  
  53.         return dic;  
  54.     }  
  55.     private static void FillStringBuilder(StringBuilder builder, SortedDictionary<stringstring> dic)  
  56.     {  
  57.         foreach (var kv in dic)  
  58.         {  
  59.             builder.Append('&');  
  60.             builder.Append(kv.Key);  
  61.             builder.Append('=');  
  62.             builder.Append(kv.Value);  
  63.         }//按key顺序组织字符串  
  64.     }  
  65.   
  66.     /// <summary>  
  67.     /// 获取32位长度的Md5摘要  
  68.     /// </summary>  
  69.     /// <param name="inputStr"></param>  
  70.     /// <param name="encoding"></param>  
  71.     /// <returns></returns>  
  72.     public static string GetMD5_32(this string inputStr, Encoding encoding = null)  
  73.     {  
  74.         RefEncoding(ref encoding);  
  75.         byte[] data = GetMD5(inputStr, encoding);  
  76.         StringBuilder tmp = new StringBuilder();  
  77.         for (int i = 0; i < data.Length; i++)  
  78.         {  
  79.             tmp.Append(data[i].ToString("x2"));  
  80.         }  
  81.         return tmp.ToString();  
  82.     }  
  83.     private static byte[] GetMD5(string inputStr, Encoding encoding)  
  84.     {  
  85.         using (MD5 md5Hash = MD5.Create())  
  86.         {  
  87.             return md5Hash.ComputeHash(encoding.GetBytes(inputStr));  
  88.         }  
  89.     }  
  90.     private static void RefEncoding(ref Encoding encoding)  
  91.     {  
  92.         if (encoding == null)  
  93.         {  
  94.             encoding = Encoding.Default;  
  95.         }  
  96.     }  
  97. }  
使用例子

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. string partner = "zhangsan";  
  2. string partnerKey = "bbb";  
  3. NameValueCollection getCollection = new NameValueCollection();  
  4. string md50 = getCollection.GetSecuritySign(partner, partnerKey);//不带任何参数的请求  
  5. getCollection.Add("test""123");  
  6. string md51 = getCollection.GetSecuritySign(partner, partnerKey);//带一个参数的请求  
  7. NameValueCollection postCollection = new NameValueCollection();  
  8. postCollection.Add("Name""张三");  
  9. postCollection.Add("Address""上海");  
  10. string md52 = getCollection.GetSecuritySign(partner, partnerKey, postCollection);//同时存在QueryString及Form的请求  
0 0
原创粉丝点击