zoukankan      html  css  js  c++  java
  • md5和SHA校验码

    md5已经不安全了,中国山东大学女学霸王小云破解了一系列密码,当真是巾帼不让须眉.说是破解,其实就是给你一个md5码,让你求出这个md5码所对应的原始信息,显然一个md5对应无数种原始信息.而md5的特性就是难以还原初始信息,但是王小云可以迅速找到给定md5码的可行解.md5的解空间虽然巨大,但是却难以捕捉到解.

    md5虽然已经不安全了,但是仍然可以用于校验.

    md5不是编码,编码是能够还原初始信息的,而md5只是固定的16byte,它只能从总体上反映初始信息的一些特征,却无法完整的还原出来,因为初始信息到md5码是一个多对一映射.

    md5中大量使用小头序,这点十分坑.还好java中有神奇伟大的ByteBuffer.ByteBuffer有四个索引:mark,limit,position,capacity.

    mark表示标记;limit表示position最大可以读到哪里;position表示当前位置;capacity表示最大容量.

    ByteBuffer这个类支持"流式"编程,比如put(byte)函数返回值仍然是ByteBuffer,这样就可以接着put了.

    ByteBuffer这个类最重要的就是4个指针,玩转了四个指针就学会了控制ByteBuffer,例如:

    flip()将limit置为当前position,将position置为0,准备从头读取数据,注意只能读取0~limit这一部分数据;

    rewind()将position置为0;

    mark()标记当前position,可以使用reset()将position重置为mark时的位置.

    compact()将当前postion之前的字节清空.

    clear()清空一切,将limit移动到copacity处,准备读入数据.

    明白了如何操作这些指针,就明白了ByteBuffer.

    当不了解md5的时候,听人们说md5的某些特性,觉得还是道听途说,只有亲自实现之后才能自己总结出md5有哪些特点.

    下面给出md5信息摘要算法

    import static java.lang.Math.abs;
    import static java.lang.Math.floor;
    import static java.lang.Math.pow;
    import static java.lang.Math.sin;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    public class MD5 {
        // 移位的位数,分成4种,每种4个数,每种重复4次.不知道为啥是这些数字
        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};
        // 在init函数中初始化k[i]
        int k[] = new int[64];
        void init() {
            for (int i = 0; i < 64; i++) {
                long x = (long) floor(abs(sin(i + 1)) * pow(2, 32));
                k[i] = (int) (x & 0xffffffff);
            }
        }
        // 循环左移操作,比如4位的1011,循环左移一位相当于0110|1
        int leftRotate(int x, int n) {
            return (x << n) | (x >>> (32 - n));
        }
        byte[] digest(byte[] srcData) throws IOException {
            // 原数据字节数+8byte的原始长度表示=l,l有点偏短,如果它不是64的倍数,那么会舍弃一些,所以要加上一;如果它是64的倍数,那么需要补上一个字节,至少要添加一个chunk.
            // 一个chunk为64byte,其中最后一个chunk的前56byte为数据+补齐的数据,最后8byte为long类型的数据,表示原始数据长度.
            // 整个md5都是基于小头序的
            int chunkCount = (srcData.length + 8) / 64 + 1;
            ByteBuffer buffer = ByteBuffer.allocate(chunkCount * 64)
                    .order(ByteOrder.LITTLE_ENDIAN);
            buffer.put(srcData).put((byte) 0x80).position(buffer.limit() - 8);
            buffer.putLong(8 * srcData.length).flip();//flip准备读取数据
            int a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
            while (buffer.hasRemaining()) {
                int m[] = new int[16];
                for (int i = 0; i < 16; i++) {
                    m[i] = buffer.getInt();
                }
                int a = a0, b = b0, c = c0, d = d0;
                int f, g;
                for (int i = 0; i < 64; i++) {
                    if (i < 16) {
                        f = (b & c) | (~b & d);
                        g = i;
                    } else if (i < 32) {
                        f = (d & b) | (~d & c);
                        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;
                    }
                    int dd = d;
                    d = c;
                    c = b;
                    b = b + leftRotate(a + f + k[i] + m[g], s[i]);
                    a = dd;
                }
                a0 += a;
                b0 += b;
                c0 += c;
                d0 += d;
            }
            return ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN).putInt(a0)
                    .putInt(b0).putInt(c0).putInt(d0).array();
        }
        static void show(byte[] b) {
            for (byte i : b) {
                System.out.printf("%02X", i);
            }
            System.out.println();
        }
        public static void main(String[] args)
                throws NoSuchAlgorithmException, IOException {
            byte[] b = "魏印福".getBytes();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(b);
            byte[] res = md5.digest();
            show(res);
            MD5 m = new MD5();
            m.init();
            res = m.digest(b);
            show(res);
        }
    }

     跟md5一样,SHA也有好多版本,SHA1,SHA2,SHA3,其中SHA2,SHA3又分成了好多版本.SHA加密强度比md5更强,它名叫Secure Hash Algorithm.md5校验码位长相当于4个int,SHA的位长相当于5个int.二者都是以64Byte为一个chunk进行处理.

    下面给出java中如何使用库函数自带的SHA

    public class SHA {
        static String tos(byte[] b) {
            String ans = "";
            for (int i = 0; i < b.length; i++) {
                ans += String.format("%02X", b[i]);
            }
            return ans;
        }
        public static void main(String[] args) throws Exception {
            byte[] data = "魏印福".getBytes();
            MessageDigest digest = MessageDigest.getInstance("SHA");
            byte[] ans = digest.digest(data);
            System.out.println(tos(ans));
            System.out
                    .println(tos(MessageDigest.getInstance("SHA-1").digest(data)));
        }
    }
  • 相关阅读:
    django控制台输出sql日志
    Find概述
    命令大全
    京东智联云在 Serverless 的探索
    如何优雅地部署一个 Serverless Next.js 应用
    腾讯云 Serverless 保障《创造营》硬糖少女 C 位出道
    Serverless 应用实践及典型案例解析
    LeetCode 数组:62. 不同路径(动态规划 带记忆的递归)
    LeetCode 数组:56.合并区间(数组的自带排序函数 区间合并问题)
    LeetCode 数组:162. 寻找峰值(二分法)
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/5911357.html
Copyright © 2011-2022 走看看