2023年12月8日发(作者:)
Java实现MD5算法过程,并利用自带MD5函数进行对比校验
文章目录
一、环境说明
操作系统:window10
编程语言:Java (JDK版本 11.0.1)
使用IDE:Intellij IDEA
二、算法原理概述
整个MD5(信息摘要算法5)的基本过程可以概括为以下几个步骤:1. 填充:消息为
K
bits的原始消息数据尾部填充长度为
P
bits的标识
<01≤P≤512(至少要填充一个bit) 。使得填充后的消
息位数满足
K+P≡448(mod512)
(注:当
K≡448(mod512)
)时,
P=512
。
填充好的消息尾部需要在附加
K
值的低64位即
Kmod2
64
。 最终结果得到
K+P+64≡0(mod512)
的填充消息。
2. 分块:把填充之后的消息结果分割为
L
个
512−bit
分组:
Y
0
..Y
L−1
。也是
L
个64字节的分组。
3. 缓冲区初始化:初始化一个
128−bit
的MD缓冲区,记为
CV
q
,表示成4个
32−bit(4个byte)
的寄存器
(A,B,C,D)
;
CV
0
=
IV(IV为16进制初值)
。
4. 循环压缩 :对L个消息分组
Y
q
(q=
0,1,...L−1)
,逐个经过4重循环的压缩算法。表示为:
CV
0
=IV
CV
i
=H
MD5
(CV
i−1
,Y
i
)
5. 得出结果:最后一个消息分组经过
H
MD5
压缩得到MD5结果为MD值,即
MD=CV
L
。
整个加密算法的基本流程如上。而整个加密算法的核心步骤在于
H
MD5
压缩函数流程如下。
1. 总控流程:
H
MD5
从
CV
输入128位,分配到缓冲区
(A,B,C,D)
,从消息分组输入512位
Y
q
,经过4轮循环,每次循环16次迭代
(共64次迭代)之后,得到用于下一轮的输入的
CV
值。如果
Yq=Y
L−1
,即输出MD5值。
2. 每轮循环:结合T表元素
T[]
和消息分组的不同部分
X[]
,每轮固定不同的生成函数
F,G,H,I
做16次迭代运算,生成下一轮循环
的输入。
3. 四个生成函数
F,G,H,I
:
4. 消息分组的内容:需要靠下标k来进行运算得到参与 迭代的消息部分,代表当前处理消息分组的第
k
个
(15)32
位字,即
M
q×16+k
。
在各轮循环中第
i
次迭代
(i=1..16)
使用的
X[k]
的确定:
设
j=i−1
:
◌ 第1轮迭代:
k=j
.
顺序使用 $ X[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]$
◌ 第2轮迭代:
k=(1+5j)mod16
.
顺序使用
X[1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12]
◌ 第3轮迭代:
k=(5+3j)mod16
.
顺序使用
X[5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2]
◌ 第4轮迭代:
k=7jmod16
.
顺序使用
X[0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9]
5. T 表元素的生成:共有64个元素,用于64次的迭代,每个元素的计算如下。
T[i]=int(2
32
×∣sin(i)∣)
int为取整函数,sin正弦函数,以i作为弧度输入。
6. 移位数s的确定:
s表共有64个元素,用于64次迭代,各次迭代运算采用的左循环移位的s 值:
s[1..16]=7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22
s[17..32]=5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20
s[33..48]=4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23
s[49..64]=6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
7. 一次迭代运算逻辑:
a,b,c,d
为寄存器
A,B,C,D
的内容,每轮循环重的一次迭代运算逻辑如下:
8. 对寄存器A进行迭代:
a←b+((a+g(b,c,d)+X[k]+T[i])<<
9. 对缓冲区的内容进行向左循环变换,即
(B,C,D,A)←(A,B,C,D)
。
X[k]
为消息分组的部分内容,
g(b,c,d)
为生成函数,
T[i]
为T表元素,
s
为移位数。
三、程序设计
数据结构
本程序使用的数据数据结构如下:
static byte[] M; /*
存放消息字节数组
*/
static long[] T = new long[64]; /*
迭代运算的
T
表,
64
个元素,每个元素有
32bits
,
16
进制
8
位
*/
/*
在迭代中的消息数组
*/
static long[] X = new long[16];
/*
四个寄存器
A
,
B
,
C
,
D
,构成
128bits
的迭代缓冲区
*/
static long A = 0x67452301;
static long B = 0xEFCDAB89;
static long C = 0x98BADCFE;
static long D = 0x10325476;
消息数组M的生成如下:
FileInputStream fis = new FileInputStream(inputString); //
读入文件流
BufferedInputStream bis = new BufferedInputStream(fis); //
将文件流读入缓冲区
DataInputStream dis = new DataInputStream(bis); //
将缓冲区数据写入数据流
M = new byte[(int) (length + paddingLength + 8)]; //
填充消息最终长度
满足于
length + padding + 8 = 0 mod 64
字节
//
将文件内容读入全部字节数组
M
中并填充
for(int i = 0; i < length + paddingLength; i++){
if( i < length){
M[i] = (byte)();
}
else if(i == length){
M[i] = (byte)128;
}
else{
M[i] = 0;
}
}
迭代运算中的T表数据生成如下:
/*
生成迭代的
T
表格
*/
public static void create_T_Table(){
for(int i = 0;i < 64;i++){
T[i] = (long) ((((i+1)) * (long)(2,32)));
}
}
处理消息分组X[]的生成如下:
/*
将
512bit
的消息处理为
16
个字的
X
数组
*/
for(j=0,k=0;j<16;j++,k+=4){
X[j] = ((int)M[i * 64 + k] & 0xFF) | ((int)M[i*64+k+1] & 0xFF) << 8 |
((int)M[i*64+k+2] & 0xFF) << 16 | ((int)M[i*64+k+3] & 0xFF) << 24;
}
重要模块步骤
循环左移s位模块
/*
* @ param x
被移数
* @ param s
左移的位数
* */
public static long rotateLeft(long x, long s){
return (x << s)| (x >> (32 - s)) & 0xFFFFFFFL;
}
/
四个生成函数和四个迭代函数。
/*
四重循环使用的函数
*/
/*
* @param a b c d
为四个缓冲区的内容
* k
为
X[k]
* s
为移位数目
* i
为
T[j]
*/
public static long F_Func(long a,long b,long c,long d,long k,long s, long i){
return (b + rotate_left(((a + ((b & c) | ((~b) & d)) + k + i) & 0xFFFFFFFFL),s)) & 0xFFFFFFFFL;
}
public static long G_Func(long a,long b,long c,long d,long k,long s, long i){
return (b + rotate_left(((a + ((b & d) | (c & (~d))) + k + i) & 0xFFFFFFFFL),s)) & 0xFFFFFFFFL;
}
public static long H_Func(long a,long b,long c,long d,long k,long s, long i){
return (b + rotate_left(((a + (b ^ c ^ d) + k + i) & 0xFFFFFFFFL) , s)) & 0xFFFFFFFFL;
}
public static long I_Func(long a,long b,long c,long d,long k,long s, long i){
return (b + rotate_left(((a + (c ^ (b | (~d))) + k + i) & 0xFFFFFFFFL), s)) & 0xFFFFFFFFL;
}
/*
将小端形式转为大端形式
*/
public static long encode(long t){
return ((t >> 24) & 0xff) | ((t >> 16) & 0xff) << 8 | ((t >> 8) & 0xff) << 16 | (t & 0xff) << 24;
}
将数据从小端转为大端的形式
/*
将小端形式转为大端形式
*/
public static long encode(long t){
return ((t >> 24) & 0xff) | ((t >> 16) & 0xff) << 8 | ((t >> 8) & 0xff) << 16 | (t & 0xff) << 24;
}
获取填充的位数
String inputString = ""; //
输入的文件名
File file = new File(inputString); //
文件操作对象
length = (); //
获取文件的字节长度
1
//
获取填充的位数
if(length % 64 < 56){
paddingLength = (int)(56 - length % 64); //
字节
}
else if(length % 64 == 56){
paddingLength = 64; // 64
字节
}
else if(length % 64 > 56){
paddingLength = (int) (64 - (length % 64 - 56));
}
将消息分块
//
该循环的作用是:对全部原始消息进行分块,每块大小为
64
个字节,共
512
位
for(int i = 0; i < (length + paddingLength + 8)/64; i++){
...
进入4次循环,共64次迭代
//
进入
4
轮循环,每次循环
16
次迭代,一共
64
次迭代
for(j = 0; j < 64; j ++){
int div16 = j / 16; // div16
代表每次循环的迭代次数
每次循环的迭代过程
switch (div16){
case 0:
//
第一轮循环,
16
次迭代
j_factor = j ;
k_index = j_factor;
//
分四个
A
、
B
、
C
、
D
缓冲区处理
if(j % 4 == 0)
{
A = F_Func(A,B,C,D,X[k_index],7,T[j]);
}
else if(j % 4 == 1)
{
D = F_Func(D,A,B,C,X[k_index],12,T[j]);
}
else if(j % 4 == 2)
{
C = F_Func(C,D,A,B,X[k_index],17,T[j]);
}
else if(j % 4 == 3)
{
B = F_Func(B,C,D,A,X[k_index],22,T[j]);
}
break;
原寄存器内容与4重循环后的寄存器内容相加,得到下一轮压缩的寄存器值。
A = (A + tmpA) & 0xFFFFFFFFL;
B = (B + tmpB) & 0xFFFFFFFFL;
C = (C + tmpC) & 0xFFFFFFFFL;
D = (D + tmpD) & 0xFFFFFFFFL;
全部消息压缩后,输出结果。
("小端形式MD5:%x %x %x %xn", A,B,C,D);
A = encode(A); //
转为大端形式
B = encode(B);
C = encode(C);
D = encode(D);
("大端形式MD5:%x %x %x %xn",A,B,C,D);
使用java自带的MD5函数进行比对验证。
/*
调用
java
自带
md5
函数
输出
md5
值
*/
public static String getMd5ForFile(String fileName) throws IOException, NoSuchAlgorithmException {
FileInputStream in = null;
File file = new File(fileName);
in = new FileInputStream(file);
MessageDigest md5 = tance("MD5");
byte[] cache = new byte[2048];
int len;
while ((len = (cache)) != -1) {
(cache, 0, len);
}
();
BigInteger bigInt = new BigInteger(1, ());
return ng(16);
}
四、运行结果
主函数中的代码如下, 利用java自带的MD5函数进行验证。
public static void main(String[] args)throws IOException, NoSuchAlgorithmException{
String inputString = ""; //
输入的文件名
n("输入加密的文件名:");
BufferedReader br = new BufferedReader(new InputStreamReader());
inputString = ne();//
直接读取字符串
getMD5ByFile(inputString); //
自行实现的函数
n("java自带的MD5函数结果如下:");
n(getMd5ForFile(inputString)); //
使用
java
自带的
MD5
函数
}
文件内容为
hello world
时,结果如下
由此可见,基本实现了MD5算法。实验存在的不足之处是:处理大文件的时候,一次性将文件数据读入内存并不太现实,Java内置的解决
办法是通过内存映射的方式来实现内存占用的问题,这为优化算法提供了新的思路。
发布评论