仅以此记录我的学习过程
MD5是单向散列函数的一种,将文本分为多个512位的分组,每个分组以32位子分组处理。算法的输出由4个32位分组组成,循环压缩将他们级联成一个128位的散列值。
首先填充消息,使其长度恰好为一个比512的倍数仅小64位的数。这64位用于记录初始消息长度。
填充的方法为:首先在消息后面附加一个1,后面接所要求多个的0,再后面为64位的记录消息的长度。
首先有初始化变量:
非线性函数如图:
共64轮迭代,前16轮为FF,后面依次GG、HH、II,每个16轮。
初始化变量A、B、C、D
//初始化寄存器
static final long A = 0x01234567;
static final long B = 0x89abcdef;
static final long C = 0xfedcba98;
static final long D = 0x76543210;
long a,b,c,d;
初始化Ti
//计算Ti代码
// for(int i = 1 ; i<=64 ; i++){
// String ti = Long.toHexString((long) (Math.pow(2,32) * Math.abs(sin(i))));
// System.out.print(ti);
// }
Ti[] = {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};
这里的Ti取
T i = 2 32 × a b s ( s i n ( i ) ) \ Ti= 2^{32} \times abs(sin(i)) Ti=232×abs(sin(i))
的整数部分。
循环移位S值
//各次迭代中采用的做循环移位的s值
static final int S[] = {
7, 12, 17, 22,7, 12, 17, 22,7, 12, 17, 22,7, 12, 17, 22,
5, 9, 14, 20,5, 9, 14, 20,5, 9, 14, 20,5, 9, 14, 20,
4, 11, 16, 23,4, 11, 16, 23,4, 11, 16, 23,4, 11, 16, 23,
6, 10, 15, 21,6, 10, 15, 21,6, 10, 15, 21,6, 10, 15, 21};
分为填充一组或是两组
//加一个分组
if (leftByte <= 56) {
fill[leftByte] = (byte) (1 << 7);
System.out.println((fill[leftByte]));
//填充过一个字节,从1开始
for (int j = 1; j < 64 - leftByte - 8; j++) {
fill[j + leftByte] = 0;
System.out.print(fill[j + leftByte] + "--------");
}
//处理分组
System.out.println(del(fill));
}
//等于56的情况
//先填一个1,后面全0后余下64bit,转为字节,补充的第一个字节即为(10000000 (bit)),后面填?个0
//填充低64 位
for (int i = 0; i < 8; i++) {
//注意:低64位存长度,但存的是长度表示,已经知道长度为len,把len这个字符串存入进去,一次取8位,
fill[i + 56] = ((byte) (bit_l & 0xFF));
System.out.print((fill[i + 56]) + "---");
bit_l = bit_l >> 8;
}
//处理分组
System.out.println();
System.out.println(del(fill));
}
//这里的leftByte为:划分完512bit的小组之后,剩余多少字节,用来判断添加的小组个数
if (leftByte > 56) {
//加两个分组,新建一个
byte[] fill_t = new byte[64];
//第一个分组,先填10000000;剩余全填0
fill[leftByte] = (byte) (1 << 7);
//这里用来测试打印出添加的10000000;
System.out.println(Byte.toUnsignedInt(fill[leftByte]));
for (int n = 1; n < 64 - leftByte; n++) {
fill[leftByte + n] = 0;
System.out.print(fill[leftByte + n] + "---");
}
System.out.println();
//处理分组
System.out.println(del(fill));
//第二个分组,填56BYTE的0,剩余为64bit长度;
for (int m = 0; m < 56; m++) {
fill_t[m] = 0;
System.out.print(fill_t[m] + "---");
}
for (int i = 0; i < 8; i++) {
//注意:低64位存长度,但存的是长度表示,已经知道长度为len,把len这个字符串存入进去,一次取8位,
fill_t[i + 56] = ((byte) (bit_l & 0xFF));
System.out.print(Byte.toUnsignedInt(fill_t[i + 56]) + "--");
bit_l = bit_l >> 8;
}
//处理分组
System.out.println(del(fill));
byte[] byte_l = message.getBytes();
long byte_m = byte_l.length;
int bits = (int) byte_m << 3;
int groups = bits / 512;
//新建一个字节数组,将每一个512分组存入并计算,计算函数为del
byte[] group_f = new byte[64];
for(int i = 0 ; i < groups ; i++){
for (int j = 0; j < 16; j++) {
group_f[j] = byte_l[j + i * 64];
}
del(group_f);
//此时计算完成每一个512分组,尾部未处理,不做输出。
//System.out.println(del(group_f));
}
//此时传入的参数均为64字节的数组。处理并返回
public String del(byte[] by) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++) {
num[j] = by[j + i * 16];
}
handle(num);
}
return changeHex(a)+changeHex(b)+changeHex(c)+changeHex(d);
}
//转十六进制
private String changeHex(long a){
String str="";
for(int i=0;i<4;i++){
str+=String.format("%2s", Long.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
}
return str;
}
取一个临时变量g,用来记录选取的分组的下表;取变量F记录F、G、H、I中之一。
将数学公式转化为代码:
private void handle(long[] num) {
long F;
int g;
a = A;
b = B;
c = C;
d = D;
for(int i = 0 ; i < 64 ; i++){
if(i<16){
F = ( b&c ) | (~b)&d ;
g = i;
} else if( i < 32){
F = (b & d) | (c & ( ~d));
g=(5*i+1)%16;
} else if( i < 48){
F = b ^ c ^ d;
g=(3*i+5)%16;
} else {
F = c ^ (b | (~d));
g=(7*i)%16;
}
a += num[g] + F + Ti[i];
a = b + ((a << S[i]) | (a >>> (32 - S[i])));
// a = b + ( (a & 0xFFFFFFFFL )<< S[i] | (a & 0xFFFFFFFFL ) >>> 32 - S[i]);
// a = b + shift(a,S[i]);
long temp = d;
d = c;
c = b;
b = a;
a = temp;
}
a += A;
b += B;
c += C;
d += D;
}
整体代码如下:
public class md5_new {
long[] num = new long[16];
static final long Ti[] =
{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};
//各次迭代中采用的做循环移位的s值
static final int S[] = {
7, 12, 17, 22,7, 12, 17, 22,7, 12, 17, 22,7, 12, 17, 22,
5, 9, 14, 20,5, 9, 14, 20,5, 9, 14, 20,5, 9, 14, 20,
4, 11, 16, 23,4, 11, 16, 23,4, 11, 16, 23,4, 11, 16, 23,
6, 10, 15, 21,6, 10, 15, 21,6, 10, 15, 21,6, 10, 15, 21};
// 初始化寄存器
static final long A = 0x01234567;
static final long B = 0x89abcdef;
static final long C = 0xfedcba98;
static final long D = 0x76543210;
long a,b,c,d;
//此时传入的参数均为64字节的数组。处理并返回
public String del(byte[] by) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++) {
num[j] = by[j + i * 16];
}
handle(num);
}
return changeHex(a)+changeHex(b)+changeHex(c)+changeHex(d);
}
//转十六进制
private String changeHex(long a){
String str="";
for(int i=0;i<4;i++){
str+=String.format("%2s", Long.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
}
return str;
}
//计算函数
private void handle(long[] num) {
long F;
int g;
a = A;
b = B;
c = C;
d = D;
for(int i = 0 ; i < 64 ; i++){
if(i<16){
F = ( b&c ) | (~b)&d ;
g = i;
} else if( i < 32){
F = (b & d) | (c & ( ~d));
g=(5*i+1)%16;
} else if( i < 48){
F = b ^ c ^ d;
g=(3*i+5)%16;
} else {
F = c ^ (b | (~d));
g=(7*i)%16;
}
a += num[g] + F + Ti[i];
a = b + ((a << S[i]) | (a >>> (32 - S[i])));
long temp = d;
d = c;
c = b;
b = a;
a = temp;
}
a += A;
b += B;
c += C;
d += D;
}
//处理添加字节函数
public void add(String message) {
int len = message.length();
//以bit表示的长度,根据这个值填低64bit
int bit_l = len << 3;
byte[] byte_l = message.getBytes();
int groups = bit_l / 512;
//新建一个字节数组,将每一个512分组存入并计算,计算函数为del
byte[] group_f = new byte[64];
for(int i = 0 ; i < groups ; i++){
for (int j = 0; j < 16; j++) {
group_f[j] = byte_l[j + i * 64];
}
del(group_f);
//此时计算完成每一个512分组,尾部未处理,不做输出。
//System.out.println(del(group_f));
}
int left = bit_l % 512;
int leftByte = left >> 3;
byte[] fill = new byte[64];
for (int j = 0; j < leftByte; j++) {
fill[j] = byte_l[j + groups * 64];
}
if (leftByte > 56) {
//加两个分组,新建一个
byte[] fill_t = new byte[64];
//第一个分组,先填10000000;剩余全填0
fill[leftByte] = (byte) (1 << 7);
for (int n = 1; n < 64 - leftByte; n++) {
fill[leftByte + n] = 0;
}
//处理分组
del(fill);
//第二个分组,填56BYTE的0,剩余为64bit长度;
for (int m = 0; m < 56; m++) {
fill_t[m] = 0;
}
for (int i = 0; i < 8; i++) {
//注意:低64位存长度,但存的是长度表示,已经知道长度为len,把len这个字符串存入进去,一次取8位,
fill_t[i + 56] = ((byte) (bit_l & 0xFF));
bit_l = bit_l >> 8;
}
//处理分组
System.out.println(del(fill));
} else {
//加一个分组
if (leftByte == 56) {
for (int i = 0; i < 8; i++) {
//注意:低64位存长度,但存的是长度表示,已经知道长度为len,把len这个字符串存入进去,一次取8位,
fill[i + 56] = ((byte) (bit_l & 0xFF));
bit_l = bit_l >> 8;
}
//处理分组
System.out.println(del(fill));
} else {
//小于56的情况
//先填一个1,后面全0后余下64bit,转为字节,补充的第一个字节即为(10000000 (bit)),后面填?个0
fill[leftByte] = (byte) (1 << 7);
//填充过一个字节,从1开始
for (int j = 1; j < 64 - leftByte - 8; j++) {
fill[j + leftByte] = 0;
}
//填充低64 位
for (int i = 0; i < 8; i++) {
//注意:低64位存长度,但存的是长度表示,已经知道长度为len,把len这个字符串存入进去,一次取8位,
fill[i + 56] = ((byte) (bit_l & 0xFF));
bit_l = bit_l >> 8;
}
//处理分组
System.out.println(del(fill));
}
}
}
public static void main(String[] args) {
md5_new md5 = new md5_new();
md5.add("16abc1326651616abc1326651616abc1326651616aabc132abc1326651616abc1326651616abc1326651616abc1326651616abc1326651616aabc1325579");
//bd14617eb347773b470fdd33e38e6fce
//测等于56
// add("16abc1326651616abc1326651616abc1326651616aabc132abc1326651616abc1326651616abc1326651616abc1326651616abc1326651616aabc132");
//测小于56
// add("16abc1326651616abc1326651616abc1326651616aabc132abc1326651616abc1326651616abc1326651616abc1326651616abc1326651616aab");
//测大于56
// add("16abc1326651616abc1326651616abc1326651616aabc132abc1326651616abc1326651616abc1326651616abc1326651616abc1326651616aabc1325579");
}
}
MD5算法的Java实现
使用java实现MD5码算法