MD5算法原理与java实现

MD5算法原理与java实现


仅以此记录我的学习过程


文章目录

  • MD5算法原理与java实现
  • MD5算法说明
    • 初始变量
    • 填充
    • 分块
    • 加密函数
  • 源代码
  • 参考资料

MD5算法说明

MD5是单向散列函数的一种,将文本分为多个512位的分组,每个分组以32位子分组处理。算法的输出由4个32位分组组成,循环压缩将他们级联成一个128位的散列值。
首先填充消息,使其长度恰好为一个比512的倍数仅小64位的数。这64位用于记录初始消息长度。
填充的方法为:首先在消息后面附加一个1,后面接所要求多个的0,再后面为64位的记录消息的长度。

首先有初始化变量:

MD5算法原理与java实现_第1张图片

非线性函数如图:
MD5算法原理与java实现_第2张图片
共64轮迭代,前16轮为FF,后面依次GG、HH、II,每个16轮。
MD5算法原理与java实现_第3张图片

初始变量

初始化变量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};

填充

分为填充一组或是两组

  • 一组: 如果去掉整数个512bit的小组,剩下的位数不超过448bit,那样就可以先填充100…00(如果正好是448bit就不用填充了),再填充低64位64bit,那样的话就只是新增了一个小组
//加一个分组
            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));
            }
  • 两组 :如果去掉整数个512bit的小组,剩下的位数超过了448bit,那样不够填充64bit,所以需要填充100…00到512bit,构成一个小组;然后再在第二个小组填充448bit个0,最后填充低64位bit,这样就会新增两个小组
//这里的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));

分块

  • 将message分为整数个group,并处理。

        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));
        }
  • 将每一个32位小分组进行计算
//此时传入的参数均为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码算法

你可能感兴趣的:(java,算法)