`
huobengluantiao8
  • 浏览: 1029394 次
文章分类
社区版块
存档分类
最新评论

简要分析用MD5加密算法加密信息(如有疑问,敬请留言)

 
阅读更多

一、引言

最近看了媒体的一篇关于“网络上公开叫卖个人隐私信息”报导,不法分子通过非法手段获得的个人隐私信息,其详细、准确程度简直令人瞠口结舌。在互联网飞速发展的现在,我们不难想到,网络肯定是传播这些个人隐私信息的重要途径之一。

现在网络上一般的网站,只要稍微完善一点的,都需要用户进行注册,提供诸如用户名、用户密码、电子邮件、甚至是电话号码、详细住址等个人隐私信息,然后才可以享受网站提供的一些特殊的信息或者服务。比如电子商务网站,用户需要购买商品,就一定需要提供详细而准确的信息,而这些信息,往往都是用户的隐私信息,比如电子邮件、电话号码、详细住址等。所以,用户注册的信息对于用户和网站来说都是很重要的资源,不能随意公开,当然也不能存在安全上的隐患。

用户注册时,如果将用户资料直接保存在数据库中,而不施加任何的保密措施,对于一些文件型数据库(如Access),如果有人得到这个数据库文件,那这些资料将全部泄露。再有,如果遇到一个不负责任的网管,则不需要任何技术手段,他就可以查看到用户的任何资料。所以,为了增加安全性,我们有必要对数据库中的隐私数据进行加密,这样,即使有人得到了整个数据库,如果没有解密算法,也一样不能查看到用户的隐私信息。但是,在考虑数据库是否安全之前,有必要对这些数据是否真的那么重要进行考虑,如果数据并不重要,则没有保密的必要,否则将会浪费系统资源、加重程序负担。反之,如果数据具有一定的隐私性,则必须进行加密。因此,在考虑加密以前,应该对数据是否需要加密做出选择,以免浪费系统资源或者留下安全隐患。

二、MD5算法简介

MD5算法,即“Message-Digest Algorithm 5(信息-摘要算法)”,它是由MD2MD3MD4发展而来的一种单向函数算法,也就是哈希(HASH)算法,是由国际著名的公钥加密算法标准RSA的第一设计者R.Rivest于上个世纪90年代初开发出来的。

加密算法一般有两种,即单向加密算法和双向加密算法。双向加密是加密算法中最常用的,它将可以直接理解的明文数据加密为不可直接理解的密文数据,在需要的时候,又可以使用一定的算法将这些加密以后的密文数据解密为原来的明文数据。双向加密适合于隐秘通讯,比如,比如在网络上注册用户或者购买商品时,提交的真实姓名、身份证号码、银行账号、信用卡密码等信息,应当通过双向加密算法加密以后,再在网络上传输,这样,可以有效的防止黑客的“偷听”,保证数据的安全,同时,网站接收到我们的数据以后,可以通过解密算法来获得准确的信息。

双向加密既可以加密,又可以解密。而单向加密则刚好相反,它只能对明文数据进行加密,而不能对加密了的密文数据再解密成原来的明文数据。可能读者会认为,不能解密的加密算法有什么作用呢?在实际应用中,对软件系统数据库中的系统用户信息(如用户密码)加密,就是一个典型的例子。当用户注册一个新的账号时,其用户密码信息不是直接保存到数据库,而是经过单向加密后再保存,这样,即使这些账号信息被泄露,别人也不能得到相应的用户密码,当然也就达不到账号的目的。

MD5算法就是单向加密的加密算法。它有两个很重要的特性,第一是任意两段明文数据,加密以后的密文数据一定是不相同的;第二是任意一段明文数据,经过加密以后,其密文数据永远是相同的。

三、MD5加密算法原理

MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest开发出来,经MD2、MD3和MD4发展而来。它的作用是让大容量信息在用数字签名软件签署私人密匙前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的大整数)。不管是MD2、MD4还是MD5,它们都需要获得一个随机长度的信息并产生一个128位的信息摘要。虽然这些算法的结构或多或少有些相似,但MD2的设计与MD4和MD5完全不同,那是因为MD2是为8位机器做过设计优化的,而MD4和MD5却是面向32位的电脑。这三个算法的描述和C语言源代码在Internet RFCs 1321中有详细的描述(http://www.ietf.org/rfc/rfc1321.txt),这是一份最权威的文档,由Ronald L. Rivest在1992年8月向IEFT提交。. .

  Van Oorschot和Wiener曾经考虑过一个在散列中暴力搜寻冲突的函数(Brute-Force Hash Function),而且他们猜测一个被设计专门用来搜索MD5冲突的机器(这台机器在1994年的制造成本大约是一百万美元)可以平均每24天就找到一个冲突。但单从1991年到2001年这10年间,竟没有出现替代MD5算法的MD6或被叫做其他什么名字的新算法这一点,我们就可以看出这个瑕疵并没有太多的影响MD5的安全性。上面所有这些都不足以成为MD5的在实际应用中的问题。并且,由于MD5算法的使用不需要支付任何版权费用的,所以在一般的情况下(非绝密应用领域。但即便是应用在绝密领域内,MD5也不失为一种非常优秀的中间技术),MD5怎么都应该算得上是非常安全的了。

  (1)
算法的应用

  MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。比如,在UNIX下有很多软件在下载的时候都有一个文件名相同,文件扩展名为.md5的文件,在这个文件中通常只有一行文本,大致结构如:

  MD5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461

  这就是tanajiya.tar.gz文件的数字签名。MD5将整个文件当作一个大文本信息,通过其不可逆的字符串变换算法,产生了这个唯一的MD5信息摘要。如果在以后传播这个文件的过程中,无论文件的内容发生了任何形式的改变(包括人为修改或者下载过程中线路不稳定引起的传输错误等),只要你对这个文件重新计算MD5时就会发现信息摘要不相同,由此可以确定你得到的只是一个不正确的文件。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的"抵赖",这就是所谓的数字签名应用。

  MD5还广泛用于加密和解密技术上。比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。

  正是因为这个原因,现在被黑客使用最多的一种破译密码的方法就是一种被称为"跑字典"的方法。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。我们假设密码的最大长度为8位字节(8 Bytes),同时密码只能是字母和数字,共26+26+10=62个字符,排列组合出的字典的项数则是P(62,1)+P(62,2)….+P(62,8),那也已经是一个很天文的数字了,存储这个字典就需要TB级的磁盘阵列,而且这种方法还有一个前提,就是能获得目标账户的密码MD5值的情况下才可以。这种加密技术被广泛的应用于UNIX系统中,这也是为什么UNIX系统比一般操作系统更为坚固一个重要原因。

  (2)
算法描述

  对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

  在MD5算法中,首先需要对信息进行填充,使其字节长度对512求余的结果等于448。因此,信息的字节长度(Bits Length)将被扩展至N*512+448,即N*64+56个字节(Bytes),N为一个正整数。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。

  MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。

  当设置好这四个链接变量后,就开始进入算法的四轮循环运算。循环的次数是信息中512位信息分组的数目。

  将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。

  主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。
以一下是每次操作中用到的四个非线性函数(每轮一个)。

   F(X,Y,Z) =(X&Y)|((~X)&Z)
   G(X,Y,Z) =(X&Z)|(Y&(~Z))
   H(X,Y,Z) =X^Y^Z
   I(X,Y,Z)=Y^(X|(~Z))
   (&是与,|是或,~是非,^是异或)

  这四个函数的说明:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。F是一个逐位运算的函数。即,如果X,那么Y,否则Z。函数H是逐位奇偶操作符。

  假设Mj表示消息的第j个子分组(从0到15),FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti),GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti),HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti),II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)

这四轮(64步)是:

  第一轮

   FF(a,b,c,d,M0,7,0xd76aa478)
   FF(d,a,b,c,M1,12,0xe8c7b756)
   FF(c,d,a,b,M2,17,0x242070db) 
FF(b,c,d,a,M3,22,0xc1bdceee)
   FF(a,b,c,d,M4,7,0xf57c0faf)
   FF(d,a,b,c,M5,12,0x4787c62a)
   FF(c,d,a,b,M6,17,0xa8304613)
   FF(b,c,d,a,M7,22,0xfd469501)
   FF(a,b,c,d,M8,7,0x698098d8)
   FF(d,a,b,c,M9,12,0x8b44f7af)
   FF(c,d,a,b,M10,17,0xffff5bb1)
   FF(b,c,d,a,M11,22,0x895cd7be)
   FF(a,b,c,d,M12,7,0x6b901122)
   FF(d,a,b,c,M13,12,0xfd987193)
   FF(c,d,a,b,M14,17,0xa679438e)
   FF(b,c,d,a,M15,22,0x49b40821) 
  第二轮

   GG(a,b,c,d,M1,5,0xf61e2562)
   GG(d,a,b,c,M6,9,0xc040b340)
   GG(c,d,a,b,M11,14,0x265e5a51)
   GG(b,c,d,a,M0,20,0xe9b6c7aa)
   GG(a,b,c,d,M5,5,0xd62f105d)
   GG(d,a,b,c,M10,9,0x02441453)
   GG(c,d,a,b,M15,14,0xd8a1e681)
   GG(b,c,d,a,M4,20,0xe7d3fbc8)
   GG(a,b,c,d,M9,5,0x21e1cde6)
   GG(d,a,b,c,M14,9,0xc33707d6)
   GG(c,d,a,b,M3,14,0xf4d50d87)
   GG(b,c,d,a,M8,20,0x455a14ed)
   GG(a,b,c,d,M13,5,0xa9e3e905)
   GG(d,a,b,c,M2,9,0xfcefa3f8)
   GG(c,d,a,b,M7,14,0x676f02d9)
   GG(b,c,d,a,M12,20,0x8d2a4c8a)

  第三轮

   HH(a,b,c,d,M5,4,0xfffa3942)
   HH(d,a,b,c,M8,11,0x8771f681)
   HH(c,d,a,b,M11,16,0x6d9d6122)
   HH(b,c,d,a,M14,23,0xfde5380c)
   HH(a,b,c,d,M1,4,0xa4beea44)
   HH(d,a,b,c,M4,11,0x4bdecfa9)
   HH(c,d,a,b,M7,16,0xf6bb4b60)
   HH(b,c,d,a,M10,23,0xbebfbc70)
   HH(a,b,c,d,M13,4,0x289b7ec6)
   HH(d,a,b,c,M0,11,0xeaa127fa)
   HH(c,d,a,b,M3,16,0xd4ef3085)
   HH(b,c,d,a,M6,23,0x04881d05)
   HH(a,b,c,d,M9,4,0xd9d4d039)
   HH(d,a,b,c,M12,11,0xe6db99e5)
   HH(c,d,a,b,M15,16,0x1fa27cf8)
   HH(b,c,d,a,M2,23,0xc4ac5665)

  第四轮

   II(a,b,c,d,M0,6,0xf4292244)
   II(d,a,b,c,M7,10,0x432aff97)
   II(c,d,a,b,M14,15,0xab9423a7)
   II(b,c,d,a,M5,21,0xfc93a039)
   II(a,b,c,d,M12,6,0x655b59c3)
   II(d,a,b,c,M3,10,0x8f0ccc92)
   II(c,d,a,b,M10,15,0xffeff47d)
   II(b,c,d,a,M1,21,0x85845dd1)
   II(a,b,c,d,M8,6,0x6fa87e4f)
   II(d,a,b,c,M15,10,0xfe2ce6e0)
   II(c,d,a,b,M6,15,0xa3014314)
   II(b,c,d,a,M13,21,0x4e0811a1)
   II(a,b,c,d,M4,6,0xf7537e82)
   II(d,a,b,c,M11,10,0xbd3af235)
   II(c,d,a,b,M2,15,0x2ad7d2bb)
   II(b,c,d,a,M9,21,0xeb86d391)


常数ti可以如下选择:

  在第i步中,ti是4294967296*abs(sin(i))的整数部分,i的单位是弧度。(4294967296等于2的32次方)
所有这些完成之后,将A、B、C、D分别加上a、b、c、d。然后用下一分组数据继续运行算法,最后的输出是A、B、C和D的级联。

  当你按照我上面所说的方法实现MD5算法以后,你可以用以下几个信息对你做出来的程序作一个简单的测试,看看程序有没有错误。

   MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
   MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
   MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
   MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
   MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
   MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
   MD5 ("123456789012345678901234567890123456789012345678901234567890123456789
01234567890") = 57edf4a22be3c955ac49da2e2107b67a

  如果你用上面的信息分别对你做的MD5算法实例做测试,最后得出的结论和标准答案完全一样,那我就要在这里象你道一声祝贺了。要知道,我的程序在第一次编译成功的时候是没有得出和上面相同的结果的。


  MD5的安全性

  MD5相对MD4所作的改进:

   1. 增加了第四轮;

   2. 每一步均有唯一的加法常数;

   3. 为减弱第二轮中函数G的对称性从(X&Y)|(X&Z)|(Y&Z)变为(X&Z)|(Y&(~Z));

   4. 第一步加上了上一步的结果,这将引起更快的雪崩效应;

   5. 改变了第二轮和第三轮中访问消息子分组的次序,使其更不相似;

   6. 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。

四、MD5加密算法(C#语言描述)

为了更好的说明MD5算法的实际应用,下面用一个实例来进行介绍。该实例使用MD5算法加密软件系统的系统用户密码。该实例用到的软件有:

n Microsoft SQL Server 2000

n Microsoft Visual Studio 2005

本实例使用C#语言开发,并采用Windows应用程序的形式给出。具体的实现方法和步骤如下:

1)使用Microsoft SQL Server 2000创建一个名为“DBMD5”数据库,并在“DBMD5”数据库中创建一个名为“tbl_User”的数据表,数据表的结构如下表所示(非本文重点,不作具体介绍)。

列名

说明

数据类型

约束

userId

用户名

varchar,长度为32

主键

userPsw

用户密码

varchar,长度为32

非空

2)使用Microsoft Visual Studio 2005创建一个名为“UseMd5”的Visual C# Windows应用程序,并将默认的窗体界面设计成如下图所示。

3)窗体和窗体中的各对象属性设置如下表所示。

控件类型

控件名称

属性

设置结果

Form

Form1

Name

FrmLogin

Text

登录

StartPosition

CenterScreen

MaximizeBox

False

Label

Label1

Text

用户名

Label2

Text

用户密码

TextBox

TextBox1

Name

txtId

TextBox2

Name

txtPsw

PasswordChar

*

Button

Button1

Name

btnLogin

Text

登录

Button2

Name

btnCancel

Text

取消

Button3

Name

btnRegedit

Text

注册

4)接下来引入几个命名空间,定义公共变量和对象,并编写一个用于加密数据的公共方法。

首先引入以下两个命名空间,其中“System.Data.SqlClient”是访问SQL数据库所需的,而“System.Security.Cryptography”则包含了与MD5算法相关的类。

using System.Data.SqlClient;
using System.Security.Cryptography;

然后定义以下公共变量和对象,其中静态字符串变量“connStr”用于指定数据库连接字符串,“conn”是SQL数据库连接对象。

public string connStr = "server=服务器名称;database=数据库名称;integrated security=SSPI";
SqlConnection conn = new SqlConnection(connStr);


接下来编写一个名为“GetMd5Str()”的方法,并给该方法定义一个字符串类型的形式参数“myString”,该方法用来将指定的字符串“myString”使用MD5算法进行加密,并返回加密后的密文数据字符串。

public string GetMd5Str(string myString)
{
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] fromData = System.Text.Encoding.Unicode.GetBytes(myString);
    byte[] toData = md5.ComputeHash(fromData);
    string byteStr = null;
    for (int i = 0; i < toData.Length; i++)
    {
        byteStr += toData[i].ToString("x");
    }
    return byteStr.Substring(0, 32);
}


上述代码中,“MD5CyptoServiceProvider”类是.NET中“System.Security.Cryptography”命名空间的一个类,提供专门用于MD5单向数据加密的解决方法,也是本文中用来加密数据库中用户密码的类。在使用“GetMd5Str()”方法进行数据加密之前,我们首先来了解“MD5CyptoServiceProvider”类的主要方法:ComputeHash()方法。ComputeHash()方法用来将输入的明文数据字符数组使用MD5进行加密,然后输出加密后的密文数据字符数组。在“GetMd5Str()”方法中:要加密的明文字符串为“myString”;用于存放明文字符串的字符数组为“fromData”;用于接收密文数据的字符数组为“toData”;方法的返回值为密文字符串“byteStr”的前32位。

可以看出,“ComputeHash()”方法只能接受数组作为加密对象,输出的密文也是数组,因此,在对字符串加密之前,我们必须首先将字符串转化为字符数组,这就要用到“Encoding”类的GetBytes方法,将字符串转化为字符数组,而加密以后的结果也是使用字符数组输出。

5)编写【取消】按钮的单击事件代码

当用户单击【取消】按钮时,即终止登录操作,并关闭登录界面,因此需要编写【取消】按钮的单击事件代码如下:

private void btnCancel_Click(object sender, EventArgs e)
{
    Application.Exit();
}


6)编写【注册】按钮的单击事件代码

当用户注册用户账号时,数据库中就需要为这个用户增加一条相应的记录。下面的程序代码实现了创建一个账号的功能,只要用户输入用户名、用户密码信息后单击【注册】按钮,就可以将这些信息存入到“DBMD 5”数据库的“tbl_User”数据表中,在这个表中,用户密码是使用MD5加密保存的。具体的代码如下:

private void btnRegedit_Click(object sender, EventArgs e)
{
    try
    {
        if (txtId.Text.Trim() == "")
        {
            MessageBox.Show("用户名不能为空!");
            txtId.Focus();
        }
        else if (txtPsw.Text.Trim() == "")
        {
            MessageBox.Show("密码不能为空!");
            txtPsw.Focus();
        }
        else
        {
            string psw = GetMd5Str(txtPsw.Text.Trim());
            string sqlStr = "insert into tbl_User values('" + txtId.Text.Trim() + "','" + psw.Trim() + "')";
            SqlCommand cmd = new SqlCommand(sqlStr, conn);
            cmd.CommandType = CommandType.Text;
            conn.Open();
            cmd.ExecuteNonQuery();
            conn.Close();
            MessageBox.Show("注册成功!");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}


由代码可知,“userPsw”字段的信息是MD5加密方式保存的,即使数据库被人取得,也不可能知道用户密码具体是什么,当然,账号信息也就不会泄露。

7)编写【登录】按钮的单击事件代码

既然用户密码是按照MD5加密以后保存在数据库中的,而前面介绍过,MD5是单向加密算法,所以,不可能将加密以后的信息转为明文,也就是说,已经没有办法知道用户密码是什么。这就出现一个问题,如果用户使用账号、密码登录系统,怎么知道用户提供的密码是否准确,从而通过系统的身份验证呢?

前面已经介绍过MD5算法的特征,任意一段明文数据,经过MD5加密以后的结果是永远不变的,也就是说,如果需要验证用户密码是否正确,只需要将用户当前提供的密码使用MD5加密,然后和数据库中保存的密码值进行比较就可以了。下面是【登录】按钮的单击事件代码,用来实现用户登录的功能:

private void btnLogin_Click(object sender, EventArgs e)
{
    try
    {
        if (txtId.Text.Trim() == "")
        {
            MessageBox.Show("用户名不能为空!");
            txtId.Focus();
        }
        else if (txtPsw.Text.Trim() == "")
        {
            MessageBox.Show("密码不能为空!");
            txtPsw.Focus();
        }
        else
        {
            string sqlStr = "select userPsw from tbl_User where userId='" + txtId.Text.Trim() + "'";
            SqlCommand cmd = new SqlCommand(sqlStr, conn);
            conn.Open();
            SqlDataReader sdr = cmd.ExecuteReader();
            if (sdr.Read())
            {
                string psw = GetMd5Str(txtPsw.Text.Trim());
                if (sdr["userPsw"].ToString().Trim() == psw.Trim())
                {
                    conn.Close();
                    MessageBox.Show("登录成功!");
                }
                else
                {
                    conn.Close();
                    MessageBox.Show("密码错误,请重新输入!");
                    txtPsw.Text = "";
                    txtPsw.Focus();
                }
            }
            else
            {
                conn.Close();
                MessageBox.Show("用户名错误,请重新输入!");
                txtId.Text = "";
                txtPsw.Text = "";
                txtId.Focus();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

看过登录代码,也许有的读者会想,黑客们盗取到用户信息后,仍然有可能先猜密码,再将猜测的密码通过MD5加密,然后用这个密文数据跟数据库中的核对,从而盗取用户账号。其实,解决这个问题的办法很简单,对于那些绝密的数据,可以通过多次加密来保存,这样,被破解的几率就几乎为零了。

五、MD5加密算法(Java语言描述)

public class MD5 
{
	 static final int S11 = 7;
     static final int S12 = 12;
     static final int S13 = 17;
     static final int S14 = 22;
     static final int S21 = 5;
     static final int S22 = 9;
     static final int S23 = 14;
     static final int S24 = 20;
     static final int S31 = 4;
     static final int S32 = 11;
     static final int S33 = 16;
     static final int S34 = 23;
     static final int S41 = 6;
     static final int S42 = 10;
     static final int S43 = 15;
     static final int S44 = 21;
     static final char Hex[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
     static final byte PADDING[] = {
         -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         0, 0, 0, 0
     };
     private long state[];
     private long count[];
     private byte buffer[];
     public String digestHexStr;
     private byte digest[];
     
     public String getMD5ofStr(String s)
     {
         int i;
         md5Init();
         md5Update(s.getBytes(), s.length());
         md5Final();
         digestHexStr = "";
         for(i = 0;i<16;i++)
           digestHexStr=digestHexStr+ByteToHex(digest[i]);
         return digestHexStr;
     }
     public MD5()
     {
         state = new long[4];
         count = new long[2];
         buffer = new byte[64];
         digest = new byte[16];
         md5Init();
     }
     private void md5Init()
     {
         count[0] = 0L;
         count[1] = 0L;
         state[0] = 0x67452301L;
         state[1] = 0xefcdab89L;
         state[2] = 0x98badcfeL;
         state[3] = 0x10325476L;
     }
     private long F(long l, long l1, long l2)
     {
         return l & l1 | ~l & l2;
     }
     private long G(long l, long l1, long l2)
     {
         return l & l2 | l1 & ~l2;
     }
     private long H(long l, long l1, long l2)
     {
         return l ^ l1 ^ l2;
     }
     private long I(long l, long l1, long l2)
     {
         return l1 ^ (l | ~l2);
     }
     private long FF(long l, long l1, long l2, long l3, long l4, long l5, long l6)
     {
         l += F(l1, l2, l3) + l4 + l6;
         l = (int)l << (int)l5 | (int)l >>> (int)(32L - l5);
         l += l1;
         return l;
     }
     private long GG(long l, long l1, long l2, long l3, long l4, long l5, long l6)
     {
         l += G(l1, l2, l3) + l4 + l6;
         l = (int)l << (int)l5 | (int)l >>> (int)(32L - l5);
         l += l1;
         return l;
     }
     private long HH(long l, long l1, long l2, long l3, long l4, long l5, long l6)
     {
         l += H(l1, l2, l3) + l4 + l6;
         l = (int)l << (int)l5 | (int)l >>> (int)(32L - l5);
         l += l1;
         return l;
     }
     private long II(long l, long l1, long l2, long l3, long l4, long l5, long l6)
     {
         l += I(l1, l2, l3) + l4 + l6;
         l = (int)l << (int)l5 | (int)l >>> (int)(32L - l5);
         l += l1;
         return l;
     }
     private void md5Update(byte abyte0[], int i)
     {
         byte abyte1[] = new byte[64];
         int k = (int)(count[0] >>> 3) & 0x3f;
         if((count[0] += i << 3) < (long)(i << 3))
             count[1]++;
         count[1] += i >>> 29;
         int l = 64 - k;
         int j;
         if(i >= l)
         {
             md5Memcpy(buffer, abyte0, k, 0, l);
             md5Transform(buffer);
             for(j = l; j + 63 < i; j += 64)
             {
                 md5Memcpy(abyte1, abyte0, 0, j, 64);
                 md5Transform(abyte1);
             }
             k = 0;
         } else
         {
             j = 0;
         }
         md5Memcpy(buffer, abyte0, k, j, i - j);
     }
     private void md5Final()
     {
         byte abyte0[] = new byte[8];
         Encode(abyte0, count, 8);
         int i = (int)(count[0] >>> 3) & 0x3f;
         int j = i >= 56 ? 120 - i : 56 - i;
         md5Update(PADDING, j);
         md5Update(abyte0, 8);
         Encode(digest, state, 16);
     }
     private void md5Memcpy(byte abyte0[], byte abyte1[], int i, int j, int k)
     {
         for(int l = 0; l < k; l++)
             abyte0[i + l] = abyte1[j + l];
     }
     private void md5Transform(byte abyte0[])
     {
         long l = state[0];
         long l1 = state[1];
         long l2 = state[2];
         long l3 = state[3];
         long al[] = new long[16];
         Decode(al, abyte0, 64);
         l = FF(l, l1, l2, l3, al[0], 7L, 0xd76aa478L);
         l3 = FF(l3, l, l1, l2, al[1], 12L, 0xe8c7b756L);
         l2 = FF(l2, l3, l, l1, al[2], 17L, 0x242070dbL);
         l1 = FF(l1, l2, l3, l, al[3], 22L, 0xc1bdceeeL);
         l = FF(l, l1, l2, l3, al[4], 7L, 0xf57c0fafL);
         l3 = FF(l3, l, l1, l2, al[5], 12L, 0x4787c62aL);
         l2 = FF(l2, l3, l, l1, al[6], 17L, 0xa8304613L);
         l1 = FF(l1, l2, l3, l, al[7], 22L, 0xfd469501L);
         l = FF(l, l1, l2, l3, al[8], 7L, 0x698098d8L);
         l3 = FF(l3, l, l1, l2, al[9], 12L, 0x8b44f7afL);
         l2 = FF(l2, l3, l, l1, al[10], 17L, 0xffff5bb1L);
         l1 = FF(l1, l2, l3, l, al[11], 22L, 0x895cd7beL);
         l = FF(l, l1, l2, l3, al[12], 7L, 0x6b901122L);
         l3 = FF(l3, l, l1, l2, al[13], 12L, 0xfd987193L);
         l2 = FF(l2, l3, l, l1, al[14], 17L, 0xa679438eL);
         l1 = FF(l1, l2, l3, l, al[15], 22L, 0x49b40821L);
         l = GG(l, l1, l2, l3, al[1], 5L, 0xf61e2562L);
         l3 = GG(l3, l, l1, l2, al[6], 9L, 0xc040b340L);
         l2 = GG(l2, l3, l, l1, al[11], 14L, 0x265e5a51L);
         l1 = GG(l1, l2, l3, l, al[0], 20L, 0xe9b6c7aaL);
         l = GG(l, l1, l2, l3, al[5], 5L, 0xd62f105dL);
         l3 = GG(l3, l, l1, l2, al[10], 9L, 0x2441453L);
         l2 = GG(l2, l3, l, l1, al[15], 14L, 0xd8a1e681L);
         l1 = GG(l1, l2, l3, l, al[4], 20L, 0xe7d3fbc8L);
         l = GG(l, l1, l2, l3, al[9], 5L, 0x21e1cde6L);
         l3 = GG(l3, l, l1, l2, al[14], 9L, 0xc33707d6L);
         l2 = GG(l2, l3, l, l1, al[3], 14L, 0xf4d50d87L);
         l1 = GG(l1, l2, l3, l, al[8], 20L, 0x455a14edL);
         l = GG(l, l1, l2, l3, al[13], 5L, 0xa9e3e905L);
         l3 = GG(l3, l, l1, l2, al[2], 9L, 0xfcefa3f8L);
         l2 = GG(l2, l3, l, l1, al[7], 14L, 0x676f02d9L);
         l1 = GG(l1, l2, l3, l, al[12], 20L, 0x8d2a4c8aL);
         l = HH(l, l1, l2, l3, al[5], 4L, 0xfffa3942L);
         l3 = HH(l3, l, l1, l2, al[8], 11L, 0x8771f681L);
         l2 = HH(l2, l3, l, l1, al[11], 16L, 0x6d9d6122L);
         l1 = HH(l1, l2, l3, l, al[14], 23L, 0xfde5380cL);
         l = HH(l, l1, l2, l3, al[1], 4L, 0xa4beea44L);
         l3 = HH(l3, l, l1, l2, al[4], 11L, 0x4bdecfa9L);
         l2 = HH(l2, l3, l, l1, al[7], 16L, 0xf6bb4b60L);
         l1 = HH(l1, l2, l3, l, al[10], 23L, 0xbebfbc70L);
         l = HH(l, l1, l2, l3, al[13], 4L, 0x289b7ec6L);
         l3 = HH(l3, l, l1, l2, al[0], 11L, 0xeaa127faL);
         l2 = HH(l2, l3, l, l1, al[3], 16L, 0xd4ef3085L);
         l1 = HH(l1, l2, l3, l, al[6], 23L, 0x4881d05L);
         l = HH(l, l1, l2, l3, al[9], 4L, 0xd9d4d039L);
         l3 = HH(l3, l, l1, l2, al[12], 11L, 0xe6db99e5L);
         l2 = HH(l2, l3, l, l1, al[15], 16L, 0x1fa27cf8L);
         l1 = HH(l1, l2, l3, l, al[2], 23L, 0xc4ac5665L);
         l = II(l, l1, l2, l3, al[0], 6L, 0xf4292244L);
         l3 = II(l3, l, l1, l2, al[7], 10L, 0x432aff97L);
         l2 = II(l2, l3, l, l1, al[14], 15L, 0xab9423a7L);
         l1 = II(l1, l2, l3, l, al[5], 21L, 0xfc93a039L);
         l = II(l, l1, l2, l3, al[12], 6L, 0x655b59c3L);
         l3 = II(l3, l, l1, l2, al[3], 10L, 0x8f0ccc92L);
         l2 = II(l2, l3, l, l1, al[10], 15L, 0xffeff47dL);
         l1 = II(l1, l2, l3, l, al[1], 21L, 0x85845dd1L);
         l = II(l, l1, l2, l3, al[8], 6L, 0x6fa87e4fL);
         l3 = II(l3, l, l1, l2, al[15], 10L, 0xfe2ce6e0L);
         l2 = II(l2, l3, l, l1, al[6], 15L, 0xa3014314L);
         l1 = II(l1, l2, l3, l, al[13], 21L, 0x4e0811a1L);
         l = II(l, l1, l2, l3, al[4], 6L, 0xf7537e82L);
         l3 = II(l3, l, l1, l2, al[11], 10L, 0xbd3af235L);
         l2 = II(l2, l3, l, l1, al[2], 15L, 0x2ad7d2bbL);
         l1 = II(l1, l2, l3, l, al[9], 21L, 0xeb86d391L);
         state[0] += l;
         state[1] += l1;
         state[2] += l2;
         state[3] += l3;
     }
     private void Encode(byte abyte0[], long al[], int i)
     {
         int j = 0;
         for(int k = 0; k < i; k += 4)
         {
             abyte0[k] = (byte)(int)(al[j] & 255L);
             abyte0[k + 1] = (byte)(int)(al[j] >>> 8 & 255L);
             abyte0[k + 2] = (byte)(int)(al[j] >>> 16 & 255L);
             abyte0[k + 3] = (byte)(int)(al[j] >>> 24 & 255L);
             j++;
         }
     }
     private void Decode(long al[], byte abyte0[], int i)
     {
         int j = 0;
         for(int k = 0; k < i; k += 4)
         {
             al[j] = ByteToLong(abyte0[k]) | ByteToLong(abyte0[k + 1]) << 8 | ByteToLong(abyte0[k + 2]) << 16 | ByteToLong(abyte0[k + 3]) << 24;
             j++;
         }
     }
     
     public static long ByteToLong(byte byte0)
     {
         return byte0 >= 0 ? byte0 : byte0 & 0xff;
     }
     
     public static String ByteToHex(byte byte0)
     {
         char ac[] = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
             'A', 'B', 'C', 'D', 'E', 'F'
         };
         char ac1[] = new char[2];
         ac1[0] = ac[byte0 >>> 4 & 0xf];
         ac1[1] = ac[byte0 & 0xf];
         String s = new String(ac1);
         return s;
     }
     
     public static String StrToHash(String s)
     {
         MD5 md5 = new MD5();
         return md5.getMD5ofStr(s).toLowerCase();
     }
}

六、MD5加密算法(标准C语言描述)

/*
  * md5 -- compute and check MD5 message digest.
  * this version only can calculate the char string.
  *
  * MD5 (Message-Digest algorithm 5) is a widely used, partially
  * insecure cryptographic hash function with a 128-bit hash value.
  *
  * Author: redraiment
  * Date: Aug 27, 2008
  * Version: 0.1.6
  */
  #include <stdlib.h>
  #include <string.h>
  #include <stdio.h>
  #include <math.h>
  #define SINGLE_ONE_BIT 0x80
  #define BLOCK_SIZE 512
  #define MOD_SIZE 448
  #define APP_SIZE 64
  #define BITS 8
  // MD5 Chaining Variable
  #define A 0x67452301UL
  #define B 0xEFCDAB89UL
  #define C 0x98BADCFEUL
  #define D 0x10325476UL
  // Creating own types
  #ifdef UINT64
  # undef UINT64
  #endif
  #ifdef UINT32
  # undef UINT32
  #endif
  typedef unsigned long long UINT64;
  typedef unsigned long UINT32;
  typedef unsigned char UINT8;
  typedef struct
  {
  char * message;
  UINT64 length;
  }STRING;
  const UINT32 X[4][2] = {{0, 1}, {1, 5}, {5, 3}, {0, 7}};
  // Constants for MD5 transform routine.
  const UINT32 S[4][4] = {
  { 7, 12, 17, 22 },
  { 5, 9, 14, 20 },
  { 4, 11, 16, 23 },
  { 6, 10, 15, 21 }
  };
  // F, G, H and I are basic MD5 functions.
  UINT32 F( UINT32 X, UINT32 Y, UINT32 Z )
  {
  return ( X & Y ) | ( ~X & Z );
  }
  UINT32 G( UINT32 X, UINT32 Y, UINT32 Z )
  {
  return ( X & Z ) | ( Y & ~Z );
  }
  UINT32 H( UINT32 X, UINT32 Y, UINT32 Z )
  {
  return X ^ Y ^ Z;
  }
  UINT32 I( UINT32 X, UINT32 Y, UINT32 Z )
  {
  return Y ^ ( X | ~Z );
  }
  // rotates x left s bits.
  UINT32 rotate_left( UINT32 x, UINT32 s )
  {
  return ( x << s ) | ( x >> ( 32 - s ) );
  }
  // Pre-processin
  UINT32 count_padding_bits ( UINT32 length )
  {
  UINT32 div = length * BITS / BLOCK_SIZE;
  UINT32 mod = length * BITS % BLOCK_SIZE;
  UINT32 c_bits;
  if ( mod == 0 )
  c_bits = MOD_SIZE;
  else
  c_bits = ( MOD_SIZE + BLOCK_SIZE - mod ) % BLOCK_SIZE;
  return c_bits / BITS;
  }
  STRING append_padding_bits ( char * argv )
  {
  UINT32 msg_length = strlen ( argv );
  UINT32 bit_length = count_padding_bits ( msg_length );
  UINT64 app_length = msg_length * BITS;
  STRING string;
  string.message = (char *)malloc(msg_length + bit_length + APP_SIZE / BITS);
  // Save message
  strncpy ( string.message, argv, msg_length );
  // Pad out to mod 64.
  memset ( string.message + msg_length, 0, bit_length );
  string.message [ msg_length ] = SINGLE_ONE_BIT;
  // Append length (before padding).
  memmove ( string.message + msg_length + bit_length, (char *)&app_length, sizeof( UINT64 ) );
  string.length = msg_length + bit_length + sizeof( UINT64 );
  return string;
  }
  int main ( int argc, char *argv[] )
  {
  STRING string;
  UINT32 w[16];
  UINT32 chain[4];
  UINT32 state[4];
  UINT8 r[16];
  UINT32 ( *auxi[ 4 ])( UINT32, UINT32, UINT32 ) = { F, G, H, I };
  int roundIdx;
  int argIdx;
  int sIdx;
  int wIdx;
  int i;
  int j;
  if ( argc < 2 )
  {
  fprintf ( stderr, "usage: %s string ...\n", argv[ 0 ] );
  return EXIT_FAILURE;
  }
  for ( argIdx = 1; argIdx < argc; argIdx++ )
  {
  string = append_padding_bits ( argv[ argIdx ] );
  // MD5 initialization.
  chain[0] = A;
  chain[1] = B;
  chain[2] = C;
  chain[3] = D;
  for ( j = 0; j < string.length; j += BLOCK_SIZE / BITS)
  {
  memmove ( (char *)w, string.message + j, BLOCK_SIZE / BITS );
  memmove ( state, chain, sizeof(chain) );
  for ( roundIdx = 0; roundIdx < 4; roundIdx++ )
  {
  wIdx = X[ roundIdx ][ 0 ];
  sIdx = 0;
  for ( i = 0; i < 16; i++ )
  {
  // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
  // Rotation is separate from addition to prevent recomputation.
  state[sIdx] = state [ (sIdx + 1) % 4 ] +
  rotate_left ( state[sIdx] +
  ( *auxi[ roundIdx ] )
  ( state[(sIdx+1) % 4], state[(sIdx+2) % 4], state[(sIdx+3) % 4]) +
  w[ wIdx ] +
  (UINT32)floor( (1UL << 32) * fabs(sin( roundIdx * 16 + i + 1 )) ),
  S[ roundIdx ][ i % 4 ]);
  sIdx = ( sIdx + 3 ) % 4;
  wIdx = ( wIdx + X[ roundIdx ][ 1 ] ) & 0xF;
  }
  }
  chain[ 0 ] += state[ 0 ];
  chain[ 1 ] += state[ 1 ];
  chain[ 2 ] += state[ 2 ];
  chain[ 3 ] += state[ 3 ];
  }
  memmove ( r + 0, (char *)&chain[0], sizeof(UINT32) );
  memmove ( r + 4, (char *)&chain[1], sizeof(UINT32) );
  memmove ( r + 8, (char *)&chain[2], sizeof(UINT32) );
  memmove ( r + 12, (char *)&chain[3], sizeof(UINT32) );
  for ( i = 0; i < 16; i++ )
  printf ( "%02x", r[i] );
  putchar ( '\n' );
  free(string.message); 
  }
  return EXIT_SUCCESS;
  }
  /* 以上程序可以在任意一款支持ANSI C的编译器上编译通过 */
  /* 直接复制粘贴,请删除多余的空格,并调整格式,否则可能有编译错误 */
  /* 在linux下编译,要添加链接库,命令如:gcc -o md5 md5.c -lm */


七、MD5加密算法(伪代码描述)


//Note: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
  var int[64] r, k //r specifies the per-round shift amounts
  r[ 0..15]:= {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22}
  r[16..31]:= {5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20}
  r[32..47]:= {4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23}
  r[48..63]:= {6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}
  //Use binary integer part of the sines of integers as constants:
  for i from 0 to 63
  k[i] := floor(abs(sin(i + 1)) × 2^32)
  //Initialize variables:
  var int h0 := 0x67452301
  var int h1 := 0xEFCDAB89
  var int h2 := 0x98BADCFE
  var int h3 := 0x10325476
  //Pre-processing:
  append "1" bit to message
  append "0" bits until message length in bits ≡ 448 (mod 512)
  append bit length of message as 64-bit little-endian integer to message
  //Process the message in successive 512-bit chunks:
  for each 512-bit chunk of message
  break chunk into sixteen 32-bit little-endian words w[i], 0 ≤ i ≤ 15
  //Initialize hash value for this chunk:
  var int a := h0
  var int b := h1
  var int c := h2
  var int d := h3
  //Main loop:
  for i from 0 to 63
  if 0 ≤ i ≤ 15 then
  f := (b and c) or ((not b) and d)
  g := i
  else if 16 ≤ i ≤ 31
  f := (d and b) or ((not d) and c)
  g := (5×i + 1) mod 16
  else if 32 ≤ i ≤ 47
  f := b xor c xor d
  g := (3×i + 5) mod 16
  else if 48 ≤ i ≤ 63
  f := c xor (b or (not d))
  g := (7×i) mod 16
  temp := d
  d := c
  c := b
  b := ((a + f + k[i] + w[g]) leftrotate r[i]) + b
  a := temp
  //Add this chunk's hash to result so far:
  h0 := h0 + a
  h1 := h1 + b
  h2 := h2 + c
  h3 := h3 + d
  var int digest := h0 append h1 append h2 append h3
  //(expressed as little-endian)


分享到:
评论

相关推荐

    MD5加密算法的一个java实例

    对md5算法简要的叙述可以为:md5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

    MD5加密算法(Java语言描述)

     对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。...

    MD5 算法的实现过程

    MD5 算法的实现过程MD5 算法的实现过程MD5 算法的实现过程

    基于单片机的实时3DES 加密算法的实现

    先简要介绍了3DES 加密算法, 接着给出了用单片机来实时实现该算 法时为了提高运算速度而采用的两种方法, 并给出了实际测试结果。

    MD5算法的原理及实现过程

    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

    java实现的加密算法

    java实现的加密算法,其中包含了加密算法的分类与加密算法在java中的简要实现,可以作为初学者的入门示例

    在JSP中如何实现MD5加密的方法

    主要使用单向散列函数算法,可用于检验消息的完整性,和通过散列密码直接以文本形式保存等,目前广泛使用的算法有MD4、MD5、SHA-1。 消息摘要有两个基本属性: 两个不同的报文难以生成相同的摘

    MD5算法(pb版 )

    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。...

    资源加密,使用方便

    MD5将整个文件当作一个大文本信息,通过其不可逆的字符串变换算法,产生了这个唯一的MD5信息摘要。为了让读者朋友对MD5的应用有个直观的认识,笔者以一个比方和一个实例来简要描述一下其工作过程: 大家都知道,地球...

    MD5算法的VB模块源码

    MD5算法 VB模块 对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位...

    利用DES加密算法保护Java源代码

    本文首先分析了Java源代码需要加密的原因,简要介绍了DES算法及Java密码体系和Java密码扩展,最后说明了利用DES加密算法保护Java源代码的方法及步骤。

    利用DES加密算法保护Java源代码.doc

    本文首先分析了Java源代码需要加密的原因,简要介绍了DES算法及Java密码体系和Java密码扩展,最后说明了利用DES加密算法保护Java源代码的方法及步骤。

    Java加密技术

    MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的 以上三种的加密并不可靠。 BASE64 按照RFC2045的定义,Base64被定义为:Base64内容传送编码被...

    md5 简单易用工具

    md5 简单易用工具,在windows命令行状态下运行即可

    论文研究-对改进的基于DNA编码和混沌的图像加密算法的安全性分析.pdf

    对一种改进的基于DNA编码和混沌映射的图像加密方法进行了安全性分析,原算法的核心思想是明文图像的DNA编码矩阵与混沌映射产生的随机矩阵的DNA编码矩阵求和,然后再对这个和矩阵中的元素随机求补即得密文图像;...

    TEA加密算法初步(英文)pdf

    关于TEA加密解密算法的简要说明。 QQ存储用户名和密码就是这个算法。

    SVN的简要介绍.md

    SVN的简要介绍及其使用

    c# 加密和解密相关代码

    添加一个Button 控件,用来使用MD5算法对输入的数据进行加密。 (3)程序主要代码如下: public string Encrypt(string strPwd) { MD5 md5 = new MD5CryptoServiceProvider(); //创建MD5 对象 byte[] data = System....

    windows7系统简要分析

    windows7系统简要分析 自己总结的,有所缺陷的

Global site tag (gtag.js) - Google Analytics