前言
网上的md5的原理和源码大部分写的太深入了,像我普通人真心看的太痛苦,决定自己写一个普通人能看的懂的
提示:以下是本篇文章正文内容,下面案例可供参考
一、md5的加密原理
就是把一字符串,通过官方自主义多个无法看的懂值,进行与,或,补位,移位等等没有人性化的操作,来回叠加折腾几圈后得到一个面目全非四不像的32位长的值(专业术语加演变),这种值当然不好破解了(专业术语加不可逆)。
1.第一步:如果是你第一步怎么做,当然是先把字符串拿过来处理一下噢,就想做面包,第一步当然是和面了
md5这个函数,是把字符串按64长度为组进行处理,什么意思,就是字符串长度+8 小于64长度就补到64; 字符串长度+8 >=64就再加长度,一致加到能存下字符串为为止,并且保持长度是64的倍数,剩下的空位补一个128,其它都是0;
细节:(1)长度+8,这8是8字符长度是为存储字符串实际长度(就了为了加密过程一个变数,折腾到无法破解“真是煞费苦心”
(2)为什么是字符串长度+8 >=64再加长度,则不是字符串长度+8 >64再加长度,(因为存长度的后8字符串位置根存储实现字符串位置中间要有一个“128”这么一个值,所以是“大于等于”,这么搞估计是他们感觉帅吧)
例子: (1)比如你要MD5加密 “123456a” ,那么字符串长度为 7,7+8 <64, 则扩展到64字符串长度,完事之后把闲置的空位补一个128,其它补0.
{ 1,2,3,4,5,6,a,"128",0,0,0,0,0,0,................0,0,0,0,0,0} /*这个空间长度是64,补了一个128,56个0*/
(2)比如你要MD5加密字符长度是56位,56+8 >=64, 则扩展到64 * 2字符串长度,完事之后把闲置的空位补一个128,其它补0.
{ ,,,,,"128",0,0,0,0,0,0,................0,0,0,0,0,0} /*这个空间长度是128, 在第57位置补一下128,其它补0 */
算法规律: (1) 字符串长度strlen 除以64向上取整N,如果 N * 64 - strlen 后发现空位大于8字符,则开辟 N * 64 个长度的数组用于存储要加密的字符串
(2) 字符串长度strlen 除以64向上取整N,如果 N * 64 - strlen 后发现空位小于等于8字符,则开辟 ((N + 1) * 64) 个长度的数组用于存储要加密的字符串
2.第二步:加入调料,把字符串长度信息加入后8字符内
第一步处理完之后,就是把字符串搞的再复杂一点,就是长度加到后8个字符串内。
例子:(1)加密的123456a, 长度为7,扩展处理完如下
{ 1,2,3,4,5,6,a,128,0,0,0,0,0,0,...........0,7,0,0,0,0,0,0,0} /*这个空间长度是64,补了一个128,56个0,把7放到后8字符串第一位上*/
(2)字符长度是56位,
{ ,,,,,128,0,0,0,0,0,0,..............0,56,0,0,0,0,0,0,0} /*这个空间长度是2个64, 在第57位置补一下128,其它补0,把56放到后8字符串第一位上 */
3.第三步:开始按官方给的固定方案进行各自不讲武德的折腾 (从这里往下就比较乏味了,毕竟规律是人家定的)
(1)把上面的数组,按64长度为一大组进行分组,再把大组中按4个字符为一小组,就是16个小组,每一小组中,第一个不变,第二个字符左移8位,第三个左移16位,第四个左移24位,然后再4个字符相互或运算(吐了)
(2)上面得到多大组,大组中有16个经过(1)折腾后的值,每个值经过4轮和特定的值进行移位,或,与等等恶心的操作折腾出一个32个字符串,
二、java原码
1.字符串处理
代码如下(示例):
private static byte[] changeToBytesTest(String str) {
int strLen = str.length();
//todo 1 总结:就是为了开辟64字符倍数的空间来存储字符串,包含后8个字符位长的空位(用于存储加符串长度)
// (1) 字符串长度 除以64向上取整N,如果 N * 64 - strlen 后发现空位大于8字符,则开辟 N * 64 个长度的数组用于存储要加密的字符串
// (2) 字符串长度 除以64向上取整N,如果 N * 64 - strlen 后发现空位小于等于8字符,则开辟 ((N + 1) * 64) 个长度的数组用于存储要加密的字符串
int cielNum = (int) Math.ceil( strLen / 64 );
int arrayLen = 0;
int strLenSave = 8;
if( cielNum * 64 - strLen <= strLenSave ){
arrayLen = ((cielNum + 1) * 64);
}else{
arrayLen = (cielNum * 64);
}
//todo 2 开辟数组空间
byte[] allBytes = new byte[ arrayLen ];
//todo 3 存储要加密的字符串的到数组中
for( int i = 0; i < strLen; ++i)
allBytes[i] = str.getBytes()[i];
//todo 4 进行补位,一个1,其它补为0
for(int j = 0; j < arrayLen - strLen ; ++j)
allBytes[strLen + j] = (j == 0) ? (byte)128 : (byte)0;
//todo 5 数组后8个元素存储加密字符串长度,分为先存低8位,高8们,以此类推
long lenr = strLen * 8;
for(int i=0; i< strLenSave ;i++){
allBytes[ arrayLen - strLenSave + i ] = (byte)lenr; //第一次为低8位
lenr = lenr >>8;//高8位左移到代8位,就得到了,哈哈哈
}
return allBytes;
}
2.全部代码
public class Md5 {
private static final long A=0x67452301L;
private static final long B=0xefcdab89L;
private static final long C=0x98badcfeL;
private static final long D=0x10325476L;
private static long[] deal = {A, B, C, D};
private 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}
};
private static final long T[] = {
0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L,
0xf61e2562L, 0xc040b340L, 0x265e5a51L, 0xe9b6c7aaL, 0xd62f105dL, 0x02441453L, 0xd8a1e681L, 0xe7d3fbc8L, 0x21e1cde6L, 0xc33707d6L, 0xf4d50d87L, 0x455a14edL, 0xa9e3e905L, 0xfcefa3f8L, 0x676f02d9L, 0x8d2a4c8aL,
0xfffa3942L, 0x8771f681L, 0x6d9d6122L, 0xfde5380cL, 0xa4beea44L, 0x4bdecfa9L, 0xf6bb4b60L, 0xbebfbc70L, 0x289b7ec6L, 0xeaa127faL, 0xd4ef3085L, 0x04881d05L, 0xd9d4d039L, 0xe6db99e5L, 0x1fa27cf8L, 0xc4ac5665L,
0xf4292244L, 0x432aff97L, 0xab9423a7L, 0xfc93a039L, 0x655b59c3L, 0x8f0ccc92L, 0xffeff47dL, 0x85845dd1L, 0x6fa87e4fL, 0xfe2ce6e0L, 0xa3014314L, 0x4e0811a1L, 0xf7537e82L, 0xbd3af235L, 0x2ad7d2bbL, 0xeb86d391L};
public static void main(String []args){
Md5 md=new Md5();
String test = "123789123456789123456789123456789123456789123456789123456789123456789123456789";
byte[] allBytes = changeToBytesTest(test);
long[][] groups = changeToLongGroups(allBytes);
//todo 9 循环处理md5压缩算法(官方规定)
for(int t = 0; t < groups.length; ++t){
recycle(groups[t]);
}
String resStr = "";
for (int i = 0; i < 4; i++) {
//解决缺少前置0的问题
resStr += String.format("%02x", deal[i] & 0xFF) +
String.format("%02x", (deal[i] & 0xFF00) >> 8) +
String.format("%02x", (deal[i] & 0xFF0000) >> 16) +
String.format("%02x", (deal[i] & 0xFF000000) >> 24);
}
System.out.println(resStr+"\n");
}
private static byte[] changeToBytesTest(String str) {
int strLen = str.length();
//todo 1 总结:就是为了开辟64字符倍数的空间来存储字符串,包含后8个字符位长的空位(用于存储加符串长度)
// (1) 字符串长度 除以64向上取整N,如果 N * 64 - strlen 后发现空位大于8字符,则开辟 N * 64 个长度的数组用于存储要加密的字符串
// (2) 字符串长度 除以64向上取整N,如果 N * 64 - strlen 后发现空位小于等于8字符,则开辟 ((N + 1) * 64) 个长度的数组用于存储要加密的字符串
int cielNum = (int) Math.ceil( strLen / 64 );
int arrayLen = 0;
int strLenSave = 8;
if( cielNum * 64 - strLen <= strLenSave ){
arrayLen = ((cielNum + 1) * 64);
}else{
arrayLen = (cielNum * 64);
}
//todo 2 开辟数组空间
byte[] allBytes = new byte[ arrayLen ];
//todo 3 存储要加密的字符串的到数组中
for( int i = 0; i < strLen; ++i)
allBytes[i] = str.getBytes()[i];
//todo 4 进行补位,一个1,其它补为0
for(int j = 0; j < arrayLen - strLen ; ++j)
allBytes[strLen + j] = (j == 0) ? (byte)128 : (byte)0;
//todo 5 数组后8个元素存储加密字符串长度,分为先存低8位,高8们,以此类推
long lenr = strLen * 8;
for(int i=0; i< strLenSave ;i++){
allBytes[ arrayLen - strLenSave + i ] = (byte)lenr; //第一次为低8位
lenr = lenr >>8;//高8位左移到代8位,就得到了,哈哈哈
}
return allBytes;
}
private static long[][] changeToLongGroups(byte[] allBytes) {
//todo 6 64个字符一大组,每大组内为16个小组,每一个小组有4个字符
int groupNum = allBytes.length / 64;
//todo 7 每一组数组长度为16位
long[][] result = new long[groupNum][16];
//todo 8 小组内 第0个字符byte 或| 第1个字符的byte * 2^8 或| 2Byte * 2^16 或| 3byte * 2^24
for(int i = 0; i < groupNum; ++i) {
for(int j=0; j<16; ++j){
result[i][j] = (offSigned(allBytes[64*i + j*4])) |
(offSigned(allBytes[64*i + j*4 + 1])) << 8|
(offSigned(allBytes[64*i + j*4 + 2])) << 16|
(offSigned(allBytes[64*i + j*4 + 3])) << 24;
}
}
return result;
}
private static void recycle(long[] group){
long a = deal[0], b = deal[1], c = deal[2], d = deal[3];
//todo 10 找规律,md5规则算法,就是这么规定的没有好说的
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 16; ++j){
int k = changeK(i, j);
if(j%4 == 0)
deal[0] = funcPoint(deal[0], deal[1], deal[2], deal[3], group[k], s[i][j], T[i*16+j], i);
else if(j%4 == 1)
deal[3] = funcPoint(deal[3], deal[0], deal[1], deal[2], group[k], s[i][j], T[i*16+j], i);
else if(j%4 == 2)
deal[2] = funcPoint(deal[2], deal[3], deal[0], deal[1], group[k], s[i][j], T[i*16+j], i);
else
deal[1] = funcPoint(deal[1], deal[2], deal[3], deal[0], group[k], s[i][j], T[i*16+j], i);
}
}
//todo 相加用于下次循环
deal[0] += a;
deal[1] += b;
deal[2] += c;
deal[3] += d;
for (int n = 0; n < 4; n++) {
deal[n] &= 0xFFFFFFFFL;
}
}
private static long funcPoint(long a, long b, long c, long d, long Mj, long s, long ti, int choose){
if(choose == 0)
return FF(a, b, c, d, Mj, s, ti);
else if(choose == 1)
return GG(a, b, c, d, Mj, s, ti);
else if(choose == 2)
return HH(a, b, c, d, Mj, s, ti);
else
return II(a, b, c, d, Mj, s, ti);
}
public static long offSigned(byte bytes){
return bytes < 0 ? bytes & 0x7F + 128 : bytes;
}
private static long F(long X, long Y, long Z) {
return (X & Y) | ((~X) & Z);
}
private static long G(long X, long Y, long Z) {
return (X & Z) | (Y & (~Z));
}
private static long H(long X, long Y, long Z) {
return X^Y^Z;
}
private static long I(long X, long Y, long Z) {
return Y ^ (X | (~Z));
}
private static long FF(long a, long b, long c, long d, long Mj, long s, long ti) {
a += (F(b, c, d) & 0xFFFFFFFFL) + Mj + ti;
a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >> (32 - s));
a += b;
return (a & 0xFFFFFFFFL);
}
private static long GG(long a, long b, long c, long d, long Mj, long s, long ti) {
a += (G(b, c, d) & 0xFFFFFFFFL) + Mj + ti;
a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >> (32 - s));
a += b;
return (a & 0xFFFFFFFFL);
}
private static long HH(long a, long b, long c, long d, long Mj, long s, long ti) {
a += (H(b, c, d) & 0xFFFFFFFFL) + Mj + ti;
a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >> (32 - s));
a += b;
return (a & 0xFFFFFFFFL);
}
private static long II(long a, long b, long c, long d, long Mj, long s, long ti) {
a += (I(b, c, d) & 0xFFFFFFFFL) + Mj + ti;
a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >> (32 - s));
a += b;
return (a & 0xFFFFFFFFL);
}
private static int changeK(int i, int j){
//todo 找规律
int k = 0;
switch(i){
case 0:
k = j;
break;
case 1:
k = (1 + 5 * j) % 16;
break;
case 2:
k = (5 + 3 * j) % 16;
break;
default:
k = (7 * j) % 16;
break;
}
return k;
}
}
3.效果对比