• spring项目中使用MD5加密方式


    百度百科中这样解释到MD5加密:

    MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途

    说起到MD5加密方式,我们先来了解下HASH算法:

    哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要

    哈希算法最重要的特点就是

    • 相同的输入一定得到相同的输出;
    • 不同的输入大概率得到不同的输出。

    哈希算法的目的就是为了验证原始数据是否被篡改

    Java字符串的hashCode()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数:

    "hello".hashCode(); // 0x5e918d2
    "hello, java".hashCode(); // 0x7a9d88e8
    "hello, bob".hashCode(); // 0xa0dbae2f

    两个相同的字符串永远会计算出相同的hashCode,否则基于hashCode定位的HashMap就无法正常工作。这也是为什么当我们自定义一个class时,覆写equals()方法时我们必须正确覆写hashCode()方法。

    哈希碰撞:

    哈希碰撞是指,两个不同的输入得到了相同的输出:

    "AaAaAa".hashCode(); // 0x7460e8c0
    "BBAaBB".hashCode(); // 0x7460e8c0 

    那么有人会问哈希碰撞是否可以避免?答案是否定的。碰撞是一定会出现的,因为输出的字节长度是固定的,StringhashCode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。

    碰撞不可怕,我们担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系到哈希算法的安全性。一个安全的哈希算法必须满足:

    • 碰撞概率低;
    • 不能猜测输出。

    不能猜测输出是指,输入的任意一个bit的变化会造成输出完全不同,这样就很难从输出反推输入(只能依靠暴力穷举)。假设一种哈希算法有如下规律:

    hashA("java001") = "123456"
    hashA("java002") = "123457"
    hashA("java003") = "123458"
    

    那么很容易从输出123459反推输入,这种哈希算法就不安全。安全的哈希算法从输出是看不出任何规律的:

    hashB("java001") = "123456"
    hashB("java002") = "580271"
    hashB("java003") = ???
    

    常用的哈希算法有:

    算法输出长度(位)输出长度(字节)
    MD5 128 bits 16 bytes
    SHA-1 160 bits 20 bytes
    RipeMD-160 160 bits 20 bytes
    SHA-256 256 bits 32 bytes
    SHA-512 512 bits 64 bytes

      

      

    根据碰撞概率,哈希算法的输出长度越长,就越难产生碰撞,也就越安全。

    Java标准库提供了常用的哈希算法,并且有一套统一的接口。我们以MD5算法为例,看看如何对输入计算哈希:

    import java.math.BigInteger;
    import java.security.MessageDigest;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            // 创建一个MessageDigest实例:
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 反复调用update输入数据:
            md.update("Hello".getBytes("UTF-8"));
            md.update("World".getBytes("UTF-8"));
            byte[] result = md.digest(); // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
            System.out.println(new BigInteger(1, result).toString(16));
        }
    }
    

    MessageDigest时,我们首先根据哈希算法获取一个MessageDigest实例,然后,反复调用update(byte[])输入数据。当输入结束后,调用digest()方法获得byte[]数组表示的摘要,最后,把它转换为十六进制的字符串。

    运行上述代码,可以得到输入HelloWorld的MD5是68e109f0f40ca72a15e05cc22786f8e6

    哈希算法的另一个重要用途是存储用户口令。如果直接将用户的原始口令存放到数据库中,会产生极大的安全风险:

    • 数据库管理员能够看到用户明文口令;
    • 数据库数据一旦泄漏,黑客即可获取用户明文口令。

    不存储用户的原始口令,那么如何对用户进行认证?

    方法是存储用户口令的哈希,例如,MD5。

    在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。

    因此,数据库存储用户名和口令的表内容应该像下面这样:

    usernamepassword
    bob f30aa7a662c728b7407c54ae6bfd27d1
    alice 25d55ad283aa400af464c76d713c07ad
    tim bed128365216c019988915ed3add75fb

    这样一来,数据库管理员看不到用户的原始口令。即使数据库泄漏,黑客也无法拿到用户的原始口令。想要拿到用户的原始口令,必须用暴力穷举的方法,一个口令一个口令地试,直到某个口令计算的MD5恰好等于指定值。使用哈希口令时,还要注意防止彩虹表攻击。

    在了解了Hash算法后我们再次回到MD5算法加密

    在spring中是自带了MD5加密的工具类的:

    在项目中搜索DigestUtils

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.springframework.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    public abstract class DigestUtils {
        private static final String MD5_ALGORITHM_NAME = "MD5";
        private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    
        public DigestUtils() {
        }
    
        public static byte[] md5Digest(byte[] bytes) {
            return digest("MD5", bytes);
        }
    
        public static byte[] md5Digest(InputStream inputStream) throws IOException {
            return digest("MD5", inputStream);
        }
    
        public static String md5DigestAsHex(byte[] bytes) {
            return digestAsHexString("MD5", bytes);
        }
    
        public static String md5DigestAsHex(InputStream inputStream) throws IOException {
            return digestAsHexString("MD5", inputStream);
        }
    
        public static StringBuilder appendMd5DigestAsHex(byte[] bytes, StringBuilder builder) {
            return appendDigestAsHex("MD5", bytes, builder);
        }
    
        public static StringBuilder appendMd5DigestAsHex(InputStream inputStream, StringBuilder builder) throws IOException {
            return appendDigestAsHex("MD5", inputStream, builder);
        }
    
        private static MessageDigest getDigest(String algorithm) {
            try {
                return MessageDigest.getInstance(algorithm);
            } catch (NoSuchAlgorithmException var2) {
                throw new IllegalStateException("Could not find MessageDigest with algorithm "" + algorithm + """, var2);
            }
        }
    
        private static byte[] digest(String algorithm, byte[] bytes) {
            return getDigest(algorithm).digest(bytes);
        }
    
        private static byte[] digest(String algorithm, InputStream inputStream) throws IOException {
            MessageDigest messageDigest = getDigest(algorithm);
            if (inputStream instanceof UpdateMessageDigestInputStream) {
                ((UpdateMessageDigestInputStream)inputStream).updateMessageDigest(messageDigest);
                return messageDigest.digest();
            } else {
                byte[] buffer = new byte[4096];
                boolean var4 = true;
    
                int bytesRead;
                while((bytesRead = inputStream.read(buffer)) != -1) {
                    messageDigest.update(buffer, 0, bytesRead);
                }
    
                return messageDigest.digest();
            }
        }
    
        private static String digestAsHexString(String algorithm, byte[] bytes) {
            char[] hexDigest = digestAsHexChars(algorithm, bytes);
            return new String(hexDigest);
        }
    
        private static String digestAsHexString(String algorithm, InputStream inputStream) throws IOException {
            char[] hexDigest = digestAsHexChars(algorithm, inputStream);
            return new String(hexDigest);
        }
    
        private static StringBuilder appendDigestAsHex(String algorithm, byte[] bytes, StringBuilder builder) {
            char[] hexDigest = digestAsHexChars(algorithm, bytes);
            return builder.append(hexDigest);
        }
    
        private static StringBuilder appendDigestAsHex(String algorithm, InputStream inputStream, StringBuilder builder) throws IOException {
            char[] hexDigest = digestAsHexChars(algorithm, inputStream);
            return builder.append(hexDigest);
        }
    
        private static char[] digestAsHexChars(String algorithm, byte[] bytes) {
            byte[] digest = digest(algorithm, bytes);
            return encodeHex(digest);
        }
    
        private static char[] digestAsHexChars(String algorithm, InputStream inputStream) throws IOException {
            byte[] digest = digest(algorithm, inputStream);
            return encodeHex(digest);
        }
    
        private static char[] encodeHex(byte[] bytes) {
            char[] chars = new char[32];
    
            for(int i = 0; i < chars.length; i += 2) {
                byte b = bytes[i / 2];
                chars[i] = HEX_CHARS[b >>> 4 & 15];
                chars[i + 1] = HEX_CHARS[b & 15];
            }
    
            return chars;
        }
    }

    以上为源代码,看到有个静态final的常量MD5_ALGORITHM_NAME和许多静态方法

    常用的是其中的md5DigestAsHex方法,它是个静态方法;

    例如密码是123456,要获得其加密后的密码:
    DigestUtils.md5DigestAsHex("123456");

    为了使得产生更独特的结果,常常在密码之前后之后加上“盐”;

    String slat = "luan_ma";
    DigestUtils.md5DigestAsHex("123456" + slat);

    以下是在一个项目中用到md5加密的方法:

    //获取当前时间和证书
        public Map<String, String> getTime() {
            Map<String, String> map = new HashMap<String, String>();
            String nowDate = DateUtil.format(new Date(), DateUtil.DATE_TIME_PATTERN);
            String date = nowDate.substring(0, 10);
            String time = nowDate.substring(11);
            String certificationKey = key + date + time;
            String md5Password = DigestUtils.md5DigestAsHex(certificationKey.getBytes());
            map.put("md5Password", md5Password);
            map.put("time", time);
            return map;
        }
    

     这样就对传递的参数按照规定的方式进行了md5加密

    参考原文:https://www.liaoxuefeng.com/wiki/1252599548343744/1304227729113121

  • 相关阅读:
    SPOJ 1812 LCS2 后缀自动机
    [APIO2014]回文串 后缀自动机_Manancher_倍增
    SPOJ8222 NSUBSTR
    [HAOI2016]找相同字符 广义后缀自动机_统计出现次数
    洛谷 P3804 【模板】后缀自动机 统计单词出现次数
    洛谷 P1368 工艺 后缀自动机 求最小表示
    力扣题目汇总(反转字符串中的单词,EXCEL表列序号,旋置矩阵)
    力扣题目汇总(重复N次元素,反转字符串,斐波那契数)
    力扣题目汇总(机器人返回原点,按奇偶排序,数字的补数)
    博客园美化的第二天(动态设置,以及结合ps制作)
  • 原文地址:https://www.cnblogs.com/zhukf/p/12887556.html
Copyright © 2020-2023  润新知