md5算法

首先我在这里对md5算法做一个简要的介绍:它全称是Message-Digest Algorithm 5,在90年代初由MIT的计算机科学实验室和RSA Data Security Inc发明,经有MD2、MD3和MD4几个低级版本。Message-Digest即信息摘要,泛指把字节串(Message)的进行Hash变换,结果是把一个任意长度的字节串变换成一定长的大整数。按照原算法的描述,md5是一个不可逆的算法,所谓的不可逆就是没有解密函数,而且存在极小概率的:多对一情况,即使我们通常验证也是用:md5(x)=md5(y)这样的方式来验证,而不是用破解函数,破解后验证原码。也因此对md5的破译的方法常为穷举法,就是算出所有的可能值然后对比,按照这种方法一个8位密码,可能有52(字母)+10(数字)+()=)20(多个特殊符号,有的可能限制作为密码)组成,这样就有大约P(82,8)= 1437064076448000 种可能,P4级别的机器,估计需要连续作战1百多万年才能全部对比完一次。除了有一定的安全性外,最值得一提的是md5算法的使用不需要支付任何版权的费用,这无疑也是最喜欢共享的兄弟们来说也是最想看到的一点。
具体算法我就不再这里罗嗦了,网上很多都有说明。
接下来说网上常用的asp加密的vbscript写的md5算法和.net算法写法。Asp的加密算法网上很多,但传来传去都是一个版本,我附在代码中供参考:该算法通常会产生一个:长度为32的16进制的数据串,其中a-f为小写。
和该asp程序对应,我写了一个c#的直接把字符串类型数据,经md5运算后也返回字符串格式的数据。程序如下[例一]
……//这里省略了系统自动引用名称空间(namespace
using System.Text;
using System.Security.Cryptography;
//这里的类名,以及名称空间之类的系统自动加的东西省略。
public string AsciibitLow(encypStr)
         {
              string retStr;
              MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();
//创建md5对象
             byte[] inputBye;
              byte[] outputBye;
              inputBye = Encoding.ASCII.GetBytes(encypStr);
//使用ascii编码方式把字符串转化为字节数组.
              outputBye = m5.ComputeHash(inputBye);
               retStr = System.BitConverter.ToString(outputBye);
              retStr = retStr.Replace("-", "").ToLower();
               return (retStr);
        }
[程序说明:]
第一、              看到这里只是为了copy代码的使用的兄弟们就不必浪费时间了。上面代码在win2003+.net2.0+vs2005测试没有问题。在.net1.1的框架中没有测试,但和本文相关应用部分.net1.1和net2.0没有多大区别,应该可以直接copy。强烈建议实际测试后使用。
第二、              System.Text,System.Security.Cryptography无论是在winform,控制台程序还是网页程序中都必须加载的两个名称空间(namespace)前者提供了Encoding编码转化转化的方法,后者则是md5等其他加密相关算法namespace。
第三、              由于系统体统提供的计算md5ComputeHash算法中,输入输出都是字节数组,所以在这里用了不同的数据类型转化方式,把字节数组中的数据转化为字符串输出。值得一提的是不同的编码方式,和不同的类型转化方式的结果不同。这就是稍后要讨论的内容。
第四、              retStr.Replace("-", "").ToLower()这一步算作是把运算结果,计算到和常用的asp中的md5算法具有相同加密结果的一步,本来可以写成System.BitConverter.ToString(outputBye) .Replace("-", "").ToLower()一步到位。想想第一太长,第二习惯vbs的兄弟们不习惯。总的来说其功能就是把BitConverter转化为形如“68-5C-FA”的字符串转化为“685cfa”的asp标准格式的字符串。
好了接下的部分我以c#的语法,简要介绍不同编码方式,以及不同类型转化方式给所带来的不同结果,以及对产生该结果的原因做一个简要的说明。稍后的内容要用下面的例子作为模板,实际应用时引用的全部名称空间同例一。
[例二]
public string AsciibitLow(encypStr)
         {
              string retStr;
              MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();
              byte[] inputBye;
              byte[] outputBye;
              inputBye = Encoding.ASCII.GetBytes(encypStr);
              outputBye = m5.ComputeHash(inputBye);
retStr = Convert.ToBase64String(outputBye);
               return (retStr);
        }
看上面的例子,标红色的部分: inputBye行(以后简称i行)是把字符串进行相应的asc编码并返回对应的字节数组的过程, retStr行(以后简称r行)把加密后的字节数组数据转化为字符串。这里通过不同的i,r行的不同,来观测其计算结果。稍后的例子中会都以字符串:“dfd!@$%127作为输入的原码
上面的[例二]返回的结果是:8j4uZKfJ2f3xH5ex12dPoQ==   
值得一提是:当i 行inputBye = Encoding.UTF8.GetBytes(encypStr);
的时候,其加密结果和[例二]返回值都是:8j4uZKfJ2f3xH5ex12dPoQ==  
[例三]
修改i,r行如下:
              inputBye = Encoding.Unicode.GetBytes(this.encypStr);
//使用Unicode编码方式把字符串转化为字节数组.
               retStr = Converter.ToBase64String (outputBye);
返回的结果是:Df9jwdVIr6bba4PKXTig2w==
这里不同的编码方式,utf8,ascii,unicode返回的数值不同,原因比较简单,前二者都是位编码的所以结果一致;后者unicode是双字节编码格式,导致在字符串转化为字节数组的时候出现了不同的结果。
 [例四]相对上面例子,对加密后结果使用了不同的类型转化方式。
inputBye = Encoding.ASCII.GetBytes(encypStr);
retStr = System.BitConverter.ToString(outputBye)
返回的结果是:F2-3E-2E-64-A7-C9-D9-FD-F1-1F-97-B1-D7-67-4F-A1
inputBye = Encoding. Unicode.GetBytes(encypStr);
retStr = System.BitConverter.ToString(outputBye)
返回的结果是:0D-FF-63-C1-D5-48-AF-A6-DB-6B-83-CA-5D-38-A0-DB
       
可以看到的一个结果:这里把md5计算后的结果用不同的转化类,转化为字符串数据的时候出现的结果也不相同,这里我就要把:Converter.ToBase64String System.BitConverter.ToString 作一个区分。
其中:
另外大家很惊讶的是:即使到了这里得到的“F2-3E-2E-64-A7-C9……”,依旧不是我们想要的结果。是呀,如果可以把“-”号去掉,大写字母转化为小写字母,你就大悟了。
看到这里,大家一定明白了,网上那个vbscript的md5只是在ascii/utf8编码后,进行md5运算,最后使用基本类型转化并去掉 “-”符号的小写的形式的字符串格式而已。在感受到.net的变化无穷的时候,和使用别人东西无奈的时候,你一定又想起了vbs的自由。算了自己写个函数来转化算了,省得到了最后一步功亏一篑。好,这里就献上看到的一位兄弟写的把加密后的byte数组转化为string的语句。
[例五]
         string retStr = “”;
MD5 md5 = MD5.Create();// 这里是另一种创建md5对象方法    
        byte[] s = md5.ComputeHash(Encoding.Unicode.GetBytes(inputBye));
        for (int i = 0; i < s.Length; i++)
        {
            // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
            retStr = retStr + s[i].ToString("x");
        }
      看到这里都感觉一定不错了,但是你知道这里有问题吗?这也是我cp到的第一个程序测试的时候,99%时候可以正常,但偶尔有失败的情况,跟踪调试后发现s[i].ToString("x")这里出问题了。很多时候这里把0x0忽略掉了,返回的数据很多时候不是标准的32位了,而是<=32位。
posted @ 2009-12-31 11:28  潇潇息一去不复返  阅读(1653)  评论(0编辑  收藏  举报
努力才能前进,勤耕博客才能看远