MD5算法的Java实现
文章目录
- 一、算法原理概述
- MD5 即Message-Digest Algorithm 5 (信息-摘要算法5)
- 基本流程
- 填充padding
- 分块
- 初始化
- 压缩函数
- 二、总体结构
- 三、模块分解
- 填充
- 分块
- 分组函数
- MD5压缩函数
- 最后结果转换为字符串
- 四、数据结构
- 五、运行结果
- 六、源代码
- 七、参考资料
一、算法原理概述
MD5 即Message-Digest Algorithm 5 (信息-摘要算法5)
- MD4 (1990)、MD5(1992, RFC 1321) 由
Ron Rivest
发明,是广泛 使用的Hash 算法,用于确保信息传输的完整性和一致性。 - MD5 使用little-endian(小端模式),输入任意不定长度信息,以 512-bit 进行分组,生成四个32-bit 数据,最后联合输出固定 128-bit 的信息摘要。
- MD5 算法的基本过程为:填充、分块、缓冲区初始化、循环压 缩、得出结果。
- MD5 不是足够安全的。
Hans Dobbertin
在1996年找到了两个不同的512-bit 块,它们 在MD5 计算下产生相同的hash
值。- 至今还没有真正找到两个不同的消息,它们的MD5 的
hash
值相等。
基本流程
填充padding
- 在长度为Kbits 的原始消息数据尾部填充长度为Pbits 的标识 100…0,1< P < 512 (即至少要填充1个bit),使得填充后的消息位 数为:K + P=448 (mod 512).
- 注意到当K=448 (mod 512) 时,需要P= 512.
- 再向上述填充好的消息尾部附加K 值的低64位(即K mod 264), 最后得到一个长度位数为K + P+ 64= 0 (mod 512) 的消息。
分块
- 把填充后的消息结果分割为L个512-bit 分组:Y0, Y1, …, YL-1。
- 分组结果也可表示成N个32-bit 字M0, M1, …, MN-1,N= Lx16。
初始化
- 初始化一个128-bit 的MD 缓冲区,记为CVq,表示成4个32-bit 寄存器(A, B, C,D);CV0= IV。迭代在MD 缓冲区进行,最后一 步的128-bit 输出即为算法结果。
压缩函数
每轮循环中的一次迭代运算逻辑
- 对A迭代:a <— b+ ((a+ g(b, c, d) + X[k] + T[i]) <<<s)
- 缓冲区(A, B, C, D) 作循环轮换: (B, C, D, A) <—(A, B, C, D)
说明:
- a, b, c, d: MD 缓冲区(A, B, C, D) 的当前值。
- g: 轮函数(F, G, H, I中的一个)。
- <<<s: 将32位输入循环左移(CLS) s位。
- X[k] : 当前处理消息分组的第k个(k= 0…15) 32位字,即 M(qx16+k)。
- T[i] : T表的第i个元素,32位字;T表总共有64个元素,也 称为加法常数。
- : 模232 加法。
二、总体结构
public class MD5 {//一系列常量数值//开始使用MD5加密private String start(String message){//分块//填充//即小于448bit的情况,先填充100...0再填充K值的低64位//此时只会新增一个分组if(rest <= 56){//填充100...0//填充K值低64位//处理分组H(divide(inputBytes, i*64));//即大于448bit的情况,先填充100...0再填充K值的低64位//此时会新增两个分组}else{//填充10000000//填充00000000//填充低64位//处理第一个尾部分组H(divide(inputBytes, i*64));//处理第二个尾部分组H(divide(inputBytes, i*64));}//将Hash值转换成十六进制的字符串//小端方式!}//从inputBytes的index开始取512位,作为新的512bit的分组private static long[] divide(byte[] inputBytes,int start){//存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用long}// groups[] 中每一个分组512位(64字节)// MD5压缩函数private void H(long[] groups) {//缓冲区(寄存器)数组//四轮循环for(int n = 0; n < 4; n++) {//16轮迭代for(int i = 0; i < 16; i++) {}}//加入之前计算的结果//防止溢出}public static void main(String []args){MD5 md=new MD5();String message = "helloMD5";System.out.println("MD5-Algorithm:\n\nOrigin Message: " + message);System.out.println("Result Message: " + md.start(message));System.out.println("Result Message(UpperCase): " + md.resultMessage.toUpperCase());//F0F99260B5A02508C71F6D81C15E9A44//3ED9E5F6855DBCDBCD95AC6C4FE0C0A5}
}
三、模块分解
填充
填充时有两种情况
- 如果去掉整数个512bit的小组,剩下的位数不超过448bit,那样就可以先填充100…00(如果正好是448bit就不用填充了),再填充用K值的低64位64bit,那样的话就只是新增了一个小组
- 如果去掉整数个512bit的小组,剩下的位数超过了448bit,那样不够填充64bit,所以需要填充100…00到512bit,构成一个小组;然后再在第二个小组填充448bit个0,最后填充K值的低64位bit,这样就会新增两个小组
//填充int rest = byteLen % 64;//即将填充的一个分组byte [] paddingBytes=new byte[64];//原来的尾部数据for(int i=0;i<rest;i++)paddingBytes[i]=inputBytes[byteLen-rest+i];//即小于448bit的情况,先填充100...0再填充K值的低64位//此时只会新增一个分组if(rest <= 56){//填充100...0if(rest<56){//填充10000000paddingBytes[rest]=(byte)(1<<7);//填充00000000for(int i=1;i<56-rest;i++)paddingBytes[rest+i]=0;}//填充K值低64位for(int i=0;i<8;i++){paddingBytes[56+i]=(byte)(K&0xFFL);K=K>>8;}//处理分组H(divide(paddingBytes,0));//即大于448bit的情况,先填充100...0再填充K值的低64位//此时会新增两个分组}else{//填充10000000paddingBytes[rest]=(byte)(1<<7);//填充00000000for(int i=rest+1;i<64;i++)paddingBytes[i]=0;//处理第一个尾部分组H(divide(paddingBytes,0));//填充00000000for(int i=0;i<56;i++)paddingBytes[i]=0;//填充低64位for(int i=0;i<8;i++){//这里很关键,使用小端方式,即Byte数组先存储len的低位数据,然后右移lenpaddingBytes[56+i]=(byte)(K&0xFFL);K=K>>8;}//处理第二个尾部分组H(divide(paddingBytes,0));}
分块
这里调用了divide
分组函数,将完整小组分成个数为16
,单个元素为32bit
的数组
再直接调用H
压缩函数,进行四轮循环和16次迭代
//分块
//完整小组(512bit)(64byte)的个数int groupCount = byteLen/64;for(int i = 0;i < groupCount;i++){//每次取512bit//处理一个分组H(divide(inputBytes, i*64));}
分组函数
这个函数传入的参数是一个字节数组,以及一个代表从哪里开始截取的int
值
效果就是将这个字节数组从start
开始的64
个字节组成一个
元素个数为16
,单个元素为32bit
的数组
采用的方法是每次取四个字节,采用小端的方式拼接成一个long
型的整数
因为int
在某些情况下是4个字节,所以就是正好32bit,但是带符号,就影响后面数据的运算
//从inputBytes的index开始取512位,作为新的512bit的分组private static long[] divide(byte[] inputBytes,int start){//存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用longlong [] group=new long[16];for(int i=0;i<16;i++){//每个32bit由4个字节拼接而来//小端的从byte数组到bit恢复方法group[i]=byte2unsign(inputBytes[4*i+start])|(byte2unsign(inputBytes[4*i+1+start]))<<8|(byte2unsign(inputBytes[4*i+2+start]))<<16|(byte2unsign(inputBytes[4*i+3+start]))<<24;}return group;}
MD5压缩函数
用了两层循环
第一层表示四轮循环
第二层表示16轮迭代
中间按照缓冲区运算的要求处理数据
这里直接处理的是result
数组,也就是真实的缓冲区,所以在开始暂存了它们当时的值为a,b,c,d
运算完毕后要加上这些值
运算过程中以及运算结束会有一些防溢出的操作
(有时候没有这个就会出错)
// groups[] 中每一个分组512位(64字节)// MD5压缩函数private void H(long[] groups) {//缓冲区(寄存器)数组long a = result[0], b = result[1], c = result[2], d = result[3];//四轮循环for(int n = 0; n < 4; n++) {//16轮迭代for(int i = 0; i < 16; i++) {result[0] += (g(n, result[1], result[2], result[3])&0xFFFFFFFFL) + groups[k[n][i]] + T[n][i];result[0] = result[1] + ((result[0]&0xFFFFFFFFL)<< S[n][i % 4] | ((result[0]&0xFFFFFFFFL) >>> (32 - S[n][i % 4])));//循环轮换long temp = result[3];result[3] = result[2];result[2] = result[1];result[1] = result[0];result[0] = temp;}}//加入之前计算的结果result[0] += a;result[1] += b;result[2] += c;result[3] += d;//防止溢出for(int n = 0; n < 4 ; n++) {result[n] &=0xFFFFFFFFL;}}
最后结果转换为字符串
之前得到的结果就是result
数组,四个元素,每个元素是一个32bit
的数据
现在要把他们转换为字符串
但是需要小端的处理方式
即long的低位作为字符串的高位
每次以一个字节处理,32bit
四个字节分别通过与运算和移位运算分离出来,再让long
的低位在前,高位在后,得到十六进制字符串就是MD5编码的结果
//将Hash值转换成十六进制的字符串//小端方式!for(int i=0;i<4;i++){resultMessage += Long.toHexString(result[i] & 0xFF) +Long.toHexString((result[i] & 0xFF00) >> 8) +Long.toHexString((result[i] & 0xFF0000) >> 16) +Long.toHexString((result[i] & 0xFF000000) >> 24);}
四、数据结构
long []groups
存储小组
String resultMessage
存储结果字符串
//四个寄存器的初始向量IV,采用小端存储
static final long A=0x67452301L
static final long B=0xefcdab89L
static final long C=0x98badcfeL
static final long D=0x10325476L
private long [] result={A,B,C,D};
模拟存储结果的四个寄存器
static final long T[][]
迭代过程中采用的近似值int(2^32 x |sin(i)|)
static final int k[][]
表示X[k]中的的k取值,决定如何使用消息分组中的字
static final int S[][]
各次迭代中采用的做循环移位的s值
private static long g(int i, long b, long c, long d)
4轮循环中使用的生成函数(轮函数)g
五、运行结果
输入原始消息:helloMD5
得到编码结果为:3ED9E5F6855DBCDBCD95AC6C4FE0C0A5
用站长之家的工具验证,结果正确
六、源代码
/*** @author Janking*/
public class MD5 {//存储小组long[] groups = null;//存储结果String resultMessage = "";//四个寄存器的初始向量IV,采用小端存储static final long A = 0x67452301L;static final long B = 0xefcdab89L;static final long C = 0x98badcfeL;static final long D = 0x10325476L;//java不支持无符号的基本数据(unsigned),所以选用long数据类型private long[] result = {A, B, C, D};static final long T[][] = {{0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821},{0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a},{0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665},{0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}};//表示X[k]中的的k取值,决定如何使用消息分组中的字static final int k[][] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},{1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12},{5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2},{0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9}};//各次迭代中采用的做循环移位的s值static final int S[][] = {{7, 12, 17, 22},{5, 9, 14, 20},{4, 11, 16, 23},{6, 10, 15, 21}};//4轮循环中使用的生成函数(轮函数)gprivate static long g(int i, long b, long c, long d) {switch (i) {case 0:return (b & c) | ((~b) & d);case 1:return (b & d) | (c & (~d));case 2:return b ^ c ^ d;case 3:return c ^ (b | (~d));default:return 0;}}//开始使用MD5加密private String start(String message) {//转化为字节数组byte[] inputBytes = message.getBytes();//6A 61 6E 6b 69 6e 67//获取字节数组的长度int byteLen = inputBytes.length;//得到K值(以bit作单位的message长度)long K = (long) (byteLen << 3);//完整小组(512bit)(64byte)的个数int groupCount = byteLen / 64;//分块for (int i = 0; i < groupCount; i++) {//每次取512bit//处理一个分组H(divide(inputBytes, i * 64));}//填充int rest = byteLen % 64;//即将填充的一个分组byte[] paddingBytes = new byte[64];//原来的尾部数据for (int i = 0; i < rest; i++)paddingBytes[i] = inputBytes[byteLen - rest + i];//即小于448bit的情况,先填充100...0再填充K值的低64位//此时只会新增一个分组if (rest <= 56) {//填充100...0if (rest < 56) {//填充10000000paddingBytes[rest] = (byte) (1 << 7);//填充00000000for (int i = 1; i < 56 - rest; i++)paddingBytes[rest + i] = 0;}//填充K值低64位for (int i = 0; i < 8; i++) {paddingBytes[56 + i] = (byte) (K & 0xFFL);K = K >> 8;}//处理分组H(divide(paddingBytes, 0));//即大于448bit的情况,先填充100...0再填充K值的低64位//此时会新增两个分组} else {//填充10000000paddingBytes[rest] = (byte) (1 << 7);//填充00000000for (int i = rest + 1; i < 64; i++)paddingBytes[i] = 0;//处理第一个尾部分组H(divide(paddingBytes, 0));//填充00000000for (int i = 0; i < 56; i++)paddingBytes[i] = 0;//填充低64位for (int i = 0; i < 8; i++) {//这里很关键,使用小端方式,即Byte数组先存储len的低位数据,然后右移lenpaddingBytes[56 + i] = (byte) (K & 0xFFL);K = K >> 8;}//处理第二个尾部分组H(divide(paddingBytes, 0));}//将Hash值转换成十六进制的字符串//小端方式!for (int i = 0; i < 4; i++) {//解决缺少前置0的问题resultMessage += String.format("%02x", result[i] & 0xFF) +String.format("%02x", (result[i] & 0xFF00) >> 8) +String.format("%02x", (result[i] & 0xFF0000) >> 16) +String.format("%02x", (result[i] & 0xFF000000) >> 24);}return resultMessage;}//从inputBytes的index开始取512位,作为新的512bit的分组private static long[] divide(byte[] inputBytes, int start) {//存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用longlong[] group = new long[16];for (int i = 0; i < 16; i++) {//每个32bit由4个字节拼接而来//小端的从byte数组到bit恢复方法group[i] = byte2unsign(inputBytes[4 * i + start]) |(byte2unsign(inputBytes[4 * i + 1 + start])) << 8 |(byte2unsign(inputBytes[4 * i + 2 + start])) << 16 |(byte2unsign(inputBytes[4 * i + 3 + start])) << 24;}return group;}//其实byte相当于一个字节的有符号整数,这里不需要符号位,所以把符号位去掉public static long byte2unsign(byte b) {return b < 0 ? b & 0x7F + 128 : b;}// groups[] 中每一个分组512位(64字节)// MD5压缩函数private void H(long[] groups) {//缓冲区(寄存器)数组long a = result[0], b = result[1], c = result[2], d = result[3];//四轮循环for (int n = 0; n < 4; n++) {//16轮迭代for (int i = 0; i < 16; i++) {result[0] += (g(n, result[1], result[2], result[3]) & 0xFFFFFFFFL) + groups[k[n][i]] + T[n][i];result[0] = result[1] + ((result[0] & 0xFFFFFFFFL) << S[n][i % 4] | ((result[0] & 0xFFFFFFFFL) >>> (32 - S[n][i % 4])));//循环轮换long temp = result[3];result[3] = result[2];result[2] = result[1];result[1] = result[0];result[0] = temp;}}//加入之前计算的结果result[0] += a;result[1] += b;result[2] += c;result[3] += d;//防止溢出for (int n = 0; n < 4; n++) {result[n] &= 0xFFFFFFFFL;}}public static void main(String[] args) {MD5 md = new MD5();String message = "helloMD5";System.out.println("MD5-Algorithm:\n\nOrigin Message: " + message);System.out.println("Result Message: " + md.start(message));System.out.println("Result Message(UpperCase): " + md.resultMessage.toUpperCase());//Origin Message: helloMD5//Result Message: 3ed9e5f6855dbcdbcd95ac6c4fe0c0a5//Result Message(UpperCase): 3ED9E5F6855DBCDBCD95AC6C4FE0C0A5}
}
七、参考资料
MD5算法原理
DES算法实现
MD5算法的Java实现相关推荐
- 【建议收藏】MD5 算法的Java Bean
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输 ...
- MD5算法了解(JAVA实现)
MD5算法:尽管已经被破解,但任然广泛应用于各个领域中 如文件校验:当我们下载文件时为了保证文件的安全性,我们能够在其站点上找到相应的md5值进行校验,假设md5值不一致,也就是说文件被人动过(一般都 ...
- java md5包_JAVA中有没有提供MD5算法的包啊?
拉莫斯之舞 有,在java.security包的MessageDigest类.例子:import java.security.MessageDigest;public class Test2 {pub ...
- java md5 算法实现_Java 实现Md5算法
package other; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /* ...
- md5解密算法 java_MD5算法解密java
参见英文答案 > Is it possible to recover message from MD5 and Java? [closed] ...
- Java的MessageDigest类、MD5算法
什么是MD5算法 MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值(hash val ...
- 基于Java实现的MD5算法实现
MD5 算法实现 一.算法原理概述 MD5 即 MD5 Message-Digest Algorithm(MD5 消息摘要算法). MD4 (1990).MD5(1992, RFC 1321) 由 R ...
- 基于java注册登录MD5算法加盐加密颁发 Token身份令牌使用各种邮箱发送验证码详解雪花算法
目的作用 == 在项目中,为了防止别人窥视我们的密码通常我们会采取一些加密方式.这里简单介绍一下MD5 加盐加密方法,MD5叫做信息-摘要算法,严格来说不是加密方式,而是信息摘要. 对于可以接触到数据 ...
- java 生成md5 8位_Java生成MD5算法
MD5即Message-Digest Algorithm 5(信息-摘要算法5),是一种用于产生数字签名的单项散列算法. MD5算法的作用是让大容量信息在用数字签名软件签私人密匙前被"压缩& ...
最新文章
- 有监督回归:最小二乘学习法
- 郑州计算机单招学校有哪些专业,郑州单招的学校有哪些专业
- JVM -- Java虚拟机
- 前端学习(1272):Vue前端路由
- Hadoop JAVA 开发说明
- zookeeper之系列五:简单操作
- SVN:show log问题
- Colaboratory平台+Mask R-CNN进行模型训练和实例分割
- ioncube php encode,ioncube加密与解密 php代码
- 海洋大数据应用关键技术及应用前景
- 计算机学报2020.1-2022.5感兴趣论文简单通览笔记
- Android CoordinatorLayout之自定义Behavior
- Ubuntu20.04微信安装 简单 使用方便
- 转阿彪 PHP常见的安全问题
- 荣耀magic3pro和华为p50pro对比哪个好
- Ubuntu18.04 设置Speaker为默认播放器
- 「掘虫者说」The timestamp difference between admin and executor exceeds the limit
- 零基础自学应用近世代数 第1章 引言和预备知识 1.1 几类实际问题 1.1.1 一些计数问题
- php导出数字0,php导出excel分析(phpexcel-避免数字前面的0消失)
- Qt Creator怎样更改默认构建目录,文件夹看起来整洁