java 源码实现md5函数,“一个说人话的程序猿”

前言

 

网上的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.效果对比

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值