MD5加密算法及Java实现

上个学期在学数据库的时候,大作业是用Java Web+MySQL实现一个简易的系统,其中老师就提到了MD5算法,用来将用户提交的密码进行加密后放在数据库中,以防被泄露。在网页上进行表单校验时,也是将数据库中用户的散列值与用户提交的密码进行映射后得到的散列值进行比对。java.security中提供了一个MessageDigest类,提供了很多此类密码散列函数,如MD5和SHA(安全散列算法 Secure Hash Algorithm)等,所以只需要调用即可,不用亲自实现。
这个暑假小学期学区块链也用到了散列算法,在比特币系统中使用的是SHA-256算法,安全性更高,为了理解这些散列算法,我选择其中相对比较简单的MD5来学习一下。

算法简介

MD5算法(Message Digest Algorithm)是一种被广泛使用的密码散列函数,其输入是任意长度的字符串,输出是这个字符串的128位(16字节)散列值(hash value)。

散列算法的本质是定义了两个集合之间的某个映射:φ:A→B
其中A是全部字符串的集合,B是全部128位二进制数的集合
显然A是无限集而B是有限集,所以理论上在A中存在两个元素,它们映射到B中的同一值,也即它们发生了碰撞,但在现实中实际使用时是用不到这么多输入的,所以碰撞的概率很低,可以忽略不计。

值得注意的是,MD5算法如今已经不再认为是一个安全的算法。有的网站提供MD5的破解方法,使用的原理是彩虹表,也就是将各个可能的输入与其对应的散列值存储起来,其存储了相当多的数据,对于某个特定的散列值,在数据库中查找其对应的原字符串即可,也就是穷举法。尽管这需要很大的数据量,但由于很多密码具有一定的相似性,比如生日、电话等等,所以很多使用MD5算法加密的密文可以被破解。


一个可以破解MD5的网站:http://www.cmd5.com/default.aspx

初始化

  1. 将输入的字符串转为二进制表示,然后通过一系列特定方法将其位数拓展为512的倍数。
  2. 初始化四个缓冲器A,B,C,D的内容,这四个缓冲器都可以理解为32位的寄存器。
  3. 将1中填充过后的信息以每512位作为一块,每块作为算法中一次循环的输入

算法步骤


一块MD5算法的执行由一个64次的循环完成,分为4组,每组16次,图中每个方框为1组,每个T[i]对应一次。

  1. 字符串填充,得到长度为512倍数的二进制串,也就是图中的message
    填充步骤:

    1. 在输入的串后面添加一个’1’
    2. 在1中得到的串后面一直添加‘0’,直到串的长度为512*n - 64,其中n为整数
    3. 求出1中输入的串的长度,将其存入一个64位的寄存器中,然后将该寄存器中的内容以小端(Little-Endian)方式存放在2中得到的串的后面。

填充完成后,将每块(512位)分为16组,每组32位,存在一个长度为16的int数组message中。

举例:如果我的输入转化了一个长度为128的位串,那么我在它后面添加1个’1’和319个’0’,此时位串的长度为448。然后原输入位串的长度是128,表示为16进制是00 00 A0 00,将它以小端方式添加到串的后面,也就是在串的后面添加00 A0 00 00,得到初始化后的位串。

至此,我们得到了一个长度为512倍数的位串。

  1. 初始化缓冲器
    初始化方法:我们的目标是在128位的缓冲器中存入如下数据:
    0x01234567 89ABCDEF FEDCBA98 76543210
    因为我们的数据在内存中是按小端方式存放的,所以应将A初始化为0x67452301,B初始化为0xEFCDAB89(long),C初始化为0x98BADCFE(long),D初始化为0x10325476(long)。

  2. 初始化T数组
    T数组是一个长度为64的int数组,其目的是为了生成一些分布比较均匀的数,在实现时使用到了正弦函数。伪代码如下:
    for i from 0 to 63
    T[i] := floor(abs(sin(i + 1)) × 2^32)

  3. 执行函数
    规则:在图中每个方框中执行它们对应的四个函数F,G,H,I,它们的输入都是B,C,D,定义如下:
    F(B,C,D)=(B∧C)∨(¬B∧D)F(B,C,D) = (B \wedge C) \vee (\neg B \wedge D)F(B,C,D)=(B∧C)∨(¬B∧D)
    G(B,C,D)=(B∧D)∨(C∧¬D)G(B,C,D) = (B \wedge D) \vee ( C \wedge \neg D)G(B,C,D)=(B∧D)∨(C∧¬D)
    H(B,C,D)=B⊕C⊕DH(B,C,D) = B \oplus C \oplus DH(B,C,D)=B⊕C⊕D
    I(B,C,D)=C⊕(B∨¬D)I(B,C,D) = C \oplus (B \vee \neg D)I(B,C,D)=C⊕(B∨¬D)

  4. 换位
    执行完第一个方框中的F函数后,得到一个结果f = F(B,C,D),现进行如下运算:
    B + ( (A + f + message[x] + T[y]) <<< s )
    其中x,y和s的具体选取是由一个数组定义好的,详见下面的源代码。
    进行完上面的运算后,将B的值放入C中,C的值放入D中,D的值放入A中,然后将上述运算的结果放入B中,完成换位。具体操作如图所示:

    执行完第五步后,在转回到第4步,进入到下一个方框中执行G函数。以此类推,直到所有函数都执行完,我们将寄存器A,B,C,D中的值组合起来,得到一个128位的Message Digest。如果MD函数的输入位串长度大于512,则将这四个寄存器的值作为下一次的输入,再执行一次上述步骤,直到将全部的位串块(512位)执行完为止;否则就将这128位的Message Digest作为输出,程序结束。

下面是这个算法的一个Java实现,代码不是我写的,是在外网上找到的,并没有调用任何的Java库,也就是说用C语言以同样的方法也能完成该算法。代码写的相当简洁,使用了很多巧妙的位运算,可以看出写这个代码的人功力非常深厚。理解这份代码可能需要一些时间,我为这份代码添加了一份注释以帮助理解和辅助记忆。

源代码:`

public class MD5 {//小端方式写入数据,使得数据在内存中看起来是规律的private static final int INIT_A = 0x67452301;private static final int INIT_B = (int)0xEFCDAB89L;private static final int INIT_C = (int)0x98BADCFEL;private static final int INIT_D = 0x10325476;//这个数组对应的是上文中循环左移的s位private static final int[] SHIFT_AMTS = {7, 12, 17, 22,5,  9, 14, 20,4, 11, 16, 23,6, 10, 15, 21};//对应上文中生成的T数组private static final int[] TABLE_T = new int[64];static{for (int i = 0; i < 64; i++)TABLE_T[i] = (int)(long)((1L << 32) * Math.abs(Math.sin(i + 1)));}public static byte[] computeMD5(byte[] message){int messageLenBytes = message.length;/**+8byte就是+64位,将结果右移6位也就是除以64,因为每64byte对应512bits,即一块。*此步求出填充后位串的总块数*/int numBlocks = ((messageLenBytes + 8) >>> 6) + 1;int totalLen = numBlocks << 6; //填充后的总字节数//初始化填充的内容:1个1和若干个0byte[] paddingBytes = new byte[totalLen - messageLenBytes];paddingBytes[0] = (byte)0x80;//初始化填充的内容:最后64位long messageLenBits = (long)messageLenBytes << 3;for (int i = 0; i < 8; i++){/**每次将一个64位的long类型数据转为byte相当于截取其最低的8位*然后将这个数右移8位,下次再截取8位,也就是原数最低的16位*/paddingBytes[paddingBytes.length - 8 + i] = (byte)messageLenBits;messageLenBits >>>= 8;}int a = INIT_A;int b = INIT_B;int c = INIT_C;int d = INIT_D;int[] buffer = new int[16]; //这里的buffer相当于前文的message数组for (int i = 0; i < numBlocks; i ++){int index = i << 6;/**将一个32位的int分为4个8位的byte*对于每个buffer[i],执行循环中的四步将其初始化*每一步得到最终32位中的8位数据*最后用或运算将这些8位的中间结果连接起来*/for (int j = 0; j < 64; j++, index++)buffer[j >>> 2] = ((int)((index < messageLenBytes) ? message[index] : paddingBytes[index - messageLenBytes]) << 24) | (buffer[j >>> 2] >>> 8);int originalA = a;int originalB = b;int originalC = c;int originalD = d;for (int j = 0; j < 64; j++){//这个循环总共执行64次,每16次为一组(一个方框),div16用来选择不同方框中的函数FGHIint div16 = j >>> 4;int f = 0;int bufferIndex = j;switch (div16){//第一组不用选择bufferindex,按顺序读取buffer数组即可case 0:f = (b & c) | (~b & d);break;//第二组,bufferIndex用来选取数组中的特定元素case 1:f = (b & d) | (c & ~d);bufferIndex = (bufferIndex * 5 + 1) & 0x0F;break;case 2:f = b ^ c ^ d;bufferIndex = (bufferIndex * 3 + 5) & 0x0F;break;case 3:f = c ^ (b | ~d);bufferIndex = (bufferIndex * 7) & 0x0F;break;}/**循环左移,rotateLeft函数中的第二个参数用来指定左移的位数*这里按照如下规则定义左移位数s*/int temp = b + Integer.rotateLeft(a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)]);//换位a = d;d = c;c = b;b = temp;}a += originalA;b += originalB;c += originalC;d += originalD;}byte[] md5 = new byte[16];int count = 0;for (int i = 0; i < 4; i++){int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d));for (int j = 0; j < 4; j++){md5[count++] = (byte)n;n >>>= 8;}}return md5;}public static String toHexString(byte[] b){StringBuilder sb = new StringBuilder();for (int i = 0; i < b.length; i++){sb.append(String.format("%02X", b[i] & 0xFF));}return sb.toString();}public static void main(String[] args){String[] testStrings = { "", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "12345678901234567890123456789012345678901234567890123456789012345678901234567890" };for (String s : testStrings)System.out.println("0x" + toHexString(computeMD5(s.getBytes())) + " <== \"" + s + "\"");System.out.println(0x67452301);return;}}

我最近发现很多以前想明白的东西因为没有及时记录下来就会很快忘记,如果将自己的思考过程记录下来可能会记得更牢固些,所以开通了这个博客。
这是我第一次写博客,发现真正想把一件事表达清楚还是比较困难的。

MD5加密算法及Java实现相关推荐

  1. MD5加密算法解释——Java简单应用(java.security.MessageDigest)

    MD5解释 MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于 ...

  2. 常用加密算法的Java实现(一) ——单向加密算法MD5和SHA

    1.Java的安全体系架构 1.1           Java的安全体系架构介绍 Java中为安全框架提供类和接口.JDK 安全 API 是 Java 编程语言的核心 API,位于 java.sec ...

  3. 常用加密算法(Java实现)总结

    1.Java的安全体系架构 Java中为安全框架提供类和接口.JDK 安全 API 是 Java 编程语言的核心 API,位于 java.security 包(及其子包),以及sun.security ...

  4. 常用加密算法的Java实现(一)

    常用加密算法的Java实现(一) --单向加密算法MD5和SHA 摘自:http://www.blogjava.net/amigoxie/archive/2014/06/01/414299.html ...

  5. MD5加密算法与SHA加密算法

    2.MD5加密 2.1 概述 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为R ...

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

    一.引言 最近看了媒体的一篇关于"网络上公开叫卖个人隐私信息"报导,不法分子通过非法手段获得的个人隐私信息,其详细.准确程度简直令人瞠口结舌.在互联网飞速发展的现在,我们不难想到, ...

  7. MD5加密算法及其在Java中的使用

    MD5算法简介 MD5是不可逆的单向加密算法,因为哈希算法是不可逆的,简单来说,就像我们可以获知5%2=1,3%2=1,7%2=1,但是,当我们仅仅拿到结果1的时候并不知道这是哪个数对2取余得到的结果 ...

  8. java md5加密长度_java中使用MD5加密算法进行加密

    java中使用MD5加密算法进行加密以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在各种应用系统的开发中 经常需要存储 ...

  9. java自带的md5加密_JDK自带MD5加密算法

    [     在各种应用系统的开发中,经常需要存储用户信息,很多地方都要存储用户密码,而将用户密码直接存储在服务器上显然是不安全的,本文简要介绍工作中常用的 MD5加密算 其实jdk就自带了md5加密算 ...

最新文章

  1. 关于kNN、kMeans、Apriori算法小结
  2. 多个Cisco产品报文远程拒绝服务漏洞
  3. UML学习---交互
  4. “科创30条”鼓励高校开设AI新兴学科
  5. python一加到二十_46 python学习笔记
  6. LeetCode 88. 合并两个有序数组(Merge Sorted Array)
  7. 【已解决】Dreamweaver修改快捷键
  8. python中的tkinter_基于python中tkinter的计算机实现
  9. java序列化kr_序列化专用列表
  10. Ubuntu 14.10 下开机不进入图形化界面
  11. 命令netstat和DHCP
  12. java list get 引用_java 关于List 对象的引用一个问题
  13. PHP 谷歌翻译类API 接口
  14. 时空平稳性,空间自相关、异质性与非平稳性
  15. java程序员创业需要_java程序员出路有哪些
  16. wordpress怎么将文章发布在指定页面?
  17. OpenHarmony社区开源代码下载及编译
  18. SpringBoot+MangoDB查询操作(MongoTemplate)总结
  19. stm32RCT6 使用NTC热敏电阻实现温度报警器
  20. 珞珈一号全国一张图相关信息及对于影像校正的一些思考

热门文章

  1. 目标检测的评估指标mAP的那些事儿
  2. 机房动环监控系统的功能,动环监控系统的主要功能
  3. 安卓系统手机音乐播放器问题
  4. 跨境电商如何减少客户流失率:成功的5种保留策略
  5. 【渝粤教育】电大中专财经法规与会计职业道德 (2)_1作业 题库
  6. 2021 Vue全家桶开发电商管理系统(Element-UI)08 商品列表功能的实现
  7. pr字幕模板 富有设计感premiere字幕条pr剪辑模板
  8. zcu111 with pynq环境下安装tensorflow
  9. linux+gunzip解压命令,Linux中的Gunzip命令详解
  10. 机器视觉,halcon项目源码,视觉检测和视觉测量源码