一、实验环境说明
操作系统:window10
编程语言:Java (JDK版本 11.0.1)
使用IDE:Intellij IDEA
二、算法原理概述
实验流程
整个MD5(信息摘要算法5)的基本过程可以概括为以下几个步骤:
填充:消息为 $K$ bits的原始消息数据尾部填充长度为$P$ bits的标识$1000…0 \, 1\le P \le 512$ (至少要填充一个bit) 。使得填充后的消息位数满足$K + P \equiv 448 (mod \, 512)$ (注:当$K \equiv 448(mod \, 512)$)时,$P = 512$。
填充好的消息尾部需要在附加 $K$值的低64位即$K \, mod \,2^{64}$。 最终结果得到 $K + P + 64 \equiv 0 (mod \, 512)$的填充消息。
分块:把填充之后的消息结果分割为$L$个$512-bit$ 分组:$Y_0..Y_{L-1}$ 。也是$L$个64字节的分组。
- 缓冲区初始化:初始化一个$128-bit$的MD缓冲区,记为$CV_q$,表示成4个$32-bit (4个byte)$ 的寄存器$(A,B,C,D)$;$CV_0 = IV (IV为16进制初值)$。
循环压缩 :对L个消息分组$Y_q(q = 0, 1,…L-1)$ ,逐个经过4重循环的压缩算法。表示为:
$$CV_0 = IV$$
$$CV_i = H_{MD5}(CV_{i-1}, Y_i)$$
得出结果:最后一个消息分组经过$H_{MD5}$压缩得到MD5结果为MD值,即$MD = CV_L$。
核心压缩步骤
- 总控流程:$H_{MD5}$ 从$CV$输入128位,分配到缓冲区$(A,B,C,D)$,从消息分组输入512位$Y_q$ ,经过4轮循环,每次循环16次迭代(共64次迭代)之后,得到用于下一轮的输入的$CV$值。如果$Yq = Y_{L-1}$,即输出MD5值。
- 每轮循环:结合T表元素$T[]$和消息分组的不同部分$X[]$,每轮固定不同的生成函数$F,G,H,I$做16次迭代运算,生成下一轮循环的输入。
- 四个生成函数$F,G,H,I$
消息分组的内容:需要靠下标k来进行运算得到参与 迭代的消息部分,代表当前处理消息分组的第$k$个$(k = 0…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) mod 16$.
顺序使用$X[1, 6,11, 0, 5,10,15, 4, 9,14, 3, 8,13, 2, 7,12]$
◌ 第3轮迭代:$k = (5 + 3j) mod 16$.
顺序使用$X[5, 8,11,14, 1, 4, 7,10,13, 0, 3, 6, 9,12,15, 2]$
◌ 第4轮迭代:$k = 7j mod 16$.
顺序使用$X[0, 7,14, 5,12, 3,10, 1, 8,15, 6,13, 4,11, 2, 9]$
T 表元素的生成:共有64个元素,用于64次的迭代,每个元素的计算如下。
$$T[i] = int(2_{32}×|sin(i)|)$$
$$ int为 取整函数,sin正弦函数,以i 作为弧度输入。$$移位数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 }$$一次迭代运算逻辑: $a,b,c,d$为寄存器$A,B,C,D$的内容,每轮循环重的一次迭代运算逻辑如下:
- 对寄存器A进行迭代:$a \leftarrow b + ((a + g(b,c,d) + X[k] + T[i]) <<< s)$
- 对缓冲区的内容进行向左循环变换,即$(B,C,D,A) \leftarrow (A,B,C,D)$。
$X[k]$为消息分组的部分内容,$g(b,c,d)$为生成函数,$T[i]$为T表元素,$s$为移位数。
三、程序设计
数据结构
本程序使用的数据数据结构如下:
1 | static byte[] M; /* 存放消息字节数组 */ |
消息数组M的生成如下:
1 | FileInputStream fis = new FileInputStream(inputString); // 读入文件流 |
迭代运算中的T表数据生成如下:
1 | /*生成迭代的T表格*/ |
处理消息分组X[]的生成如下:
1 | /*将 512bit的消息处理为 16个字的X数组*/ |
重要模块步骤
循环左移s位模块
1 | /* |
四个生成函数和四个迭代函数。
1 | /*四重循环使用的函数*/ |
将数据从小端转为大端的形式
1 | /*将小端形式转为大端形式*/ |
获取填充的位数
1 | String inputString = "test1.txt"; // 输入的文件名 |
将消息分块
1 | // 该循环的作用是:对全部原始消息进行分块,每块大小为64个字节,共512位 |
进入4次循环,共64次迭代
1 | // 进入 4 轮循环,每次循环16次迭代,一共64次迭代 |
每次循环的迭代过程
1 | switch (div16){ |
原寄存器内容与4重循环后的寄存器内容相加,得到下一轮压缩的寄存器值。
1 | A = (A + tmpA) & 0xFFFFFFFFL; |
全部消息压缩后,输出结果。
1 | System.out.format("小端形式MD5:%x %x %x %x\n", A,B,C,D); |
使用java自带的MD5函数进行比对验证。
1 | /*调用java自带md5函数 输出md5值*/ |