数据结构和算法(二):摘要算法之SHA和MD5

几种常用的加密算法,篇幅的百分之九十都是从其他博客摘抄的,因为就我自己的能力,是没法写全这些内容的。另外这些算法内部深层的原理就不解释了(看过相关博客和公众号,看不懂,尴尬),只做简单的介绍,相信这样简单的介绍已经可以让你在工作中很好的使用啦。

这篇文章我们将会说到SHA、MD5两种加密算法,日常的开发中经常被使用到,就目前个人接触的项目中用的最多的就是MD5。那么接下来就具体来说一下这两种加密算法。

借鉴小灰公众号两篇文章:

漫画:什么是MD5算法?

漫画:什么是SHA系列算法?

摘要算法之MD5

MD5加密算法从使用上是很简单的,JDK有MD5的加密类,但是使用起来比较麻烦,其加密方法返回的是长度为16二进制数组,需要将二进制数组转换成16进制,然后转换成对应的字符串。

另外Spring对MD5也有封装,其实就是对JDK中MD5加密类进行封装,执行上面提到的转换步骤。下面来了解一下自己实现转换和直接使用Spring封装的转换方法。

JDK自带MD5加密类转换工具类

代码如下:

public class Md5Utils {

    private static MessageDigest MD5_DIGEST;
    private static char[] LOWER_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static char[] UPPER_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    static {
        try {
            MD5_DIGEST = MessageDigest.getInstance("MD5");//初始化MD加密实例
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
	// 大写字母加密串
    public static String upperMd5(String source) {
        if (StringUtils.isBlank(source)) throw new RuntimeException("source is blank");
        StringBuilder sb = new StringBuilder();
        for (byte secretByte : MD5_DIGEST.digest(source.getBytes())) {
            sb.append(UPPER_CHARS[(secretByte >> 4) & 15]);
            sb.append(UPPER_CHARS[secretByte & 15]);
        }
        return sb.toString();
    }
	//小写字母加密传
    public static String lowerMd5(String source) {
        if (StringUtils.isBlank(source)) throw new RuntimeException("source is blank");
        StringBuilder sb = new StringBuilder();
        for (byte secretByte : MD5_DIGEST.digest(source.getBytes())) {
            sb.append(LOWER_CHARS[(secretByte >> 4) & 15]);
            sb.append(LOWER_CHARS[secretByte & 15]);
        }
        return sb.toString();
    }
}

在不同的公司,有的习惯使用小写字母加密串,有的则用大写字母,所以上面提供两种方法。其实加密结果基本一样,大写字母加密串就是将小写字母加密结果执行一下toUpperCase()方法。

Spring封装后的MD5加密工具类

这里看一下其内部的实现源码。使用是调用DigestUtilsmd5DigestAsHex方法,那就以这个方法为入口看源码。

public static String md5DigestAsHex(byte[] bytes) {
    //MD5_ALGORITHM_NAME是常量,对应的值是"MD5"
    return digestAsHexString(MD5_ALGORITHM_NAME, bytes);//进入digestAsHexString方法
}
private static String digestAsHexString(String algorithm, byte[] bytes) {
    char[] hexDigest = digestAsHexChars(algorithm, bytes);//进入digestAsHexChars方法
    return new String(hexDigest);
}
private static char[] digestAsHexChars(String algorithm, byte[] bytes) {
    byte[] digest = digest(algorithm, bytes);//进入digest方法
    return encodeHex(digest);//进入encodeHex方法
}
private static byte[] digest(String algorithm, byte[] bytes) {
    return getDigest(algorithm).digest(bytes);//进入getDigest方法,使用MessageDigest的digest方法加密
}
private static MessageDigest getDigest(String algorithm) {
    try {
        return MessageDigest.getInstance(algorithm);//这里就可以看出来使用的是JDK自带的加密工具类
    }
    catch (NoSuchAlgorithmException ex) {
        throw new IllegalStateException("Could not find MessageDigest with algorithm \"" + algorithm + "\"", ex);
    }
}
private static char[] encodeHex(byte[] bytes) { //对二进制数组转换成16进制字符数组
    char chars[] = new char[32];
    for (int i = 0; i < chars.length; i = i + 2) {
        byte b = bytes[i / 2];
        chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
        chars[i + 1] = HEX_CHARS[b & 0xf];
    }
    return chars;
}

从这段源码,然后对照上面对JDK中MD5加密类的封装,两者基本都是同样的逻辑,主要的转换逻辑都是一样的。所以就不难理解啦。唯一的不同就是Spring没有做大小写字母的特殊封装,默认就是小写字母。

加密出来的二进制数组长度是16,在处理的过程中,经过两次不同的位运算,得到两个字符,最终形成32位的字符串。

关于MD5的破解

MD5是单向加密,是不可逆向解密,但是为什么网上有那么多的MD5解密工具呢?不知道大家有没有注意,这些解密工具都不能立即给出解密结果或者在很短时间内给出(除非常用的MD5加密串,有对应的记录)。因为这些解密工具都是通过碰撞方式来破解,也就是暴力破解。通过排列组合各种字符,然后转换成MD5,将这个MD5值和我们提供的MD5值进行比对,对了就给出结果,不对接着尝试。

其实这种方式根本不是所谓的破解之法。在我个人的理解范围内,MD5破解依然是不可能的。(如果真的有对应的破解之法,网络上肯定有破解的代码,MD5也不会继续被使用,而被淘汰)

摘要算法之SHA

摘要算法SHA同MD5也是单向加密,是不可逆向解密,SHA算法有很多版本,大的版本有SHA-1、SHA-2,在SHA-2中又包含SHA-256、SHA-224、SHA-384、SHA-512,其中SHA-224、SHA-384分别是SHA-256、SHA-512的阉割版。分别是在于生成长度的不同。

SHA-1在早期已经被破解,现在已经被淘汰,Chrome随后推出了SHA-2版本。目前使用SHA算法,都是SHA-2下的子版本,用的最多的应该就是SHA-256。下面来一起了解一下SHA算法。

在JDK里面其实是同样支持SHA加密,其使用方法和MD5基本相同,不同的是在getInstance方法中传入的参数,分别是MD5或SHA-256(SHA-224等)。下面简单的写一下加密的操作。

private static char[] LOWER_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

public static void main(String[] args) throws NoSuchAlgorithmException {
    //JDK自带的
    String text = "123456";
    MessageDigest sha = MessageDigest.getInstance("SHA-256");
    byte[] digest = sha.digest(text.getBytes());//加密得到的是32位byte数组
    StringBuilder sb = new StringBuilder();
    for (byte secretByte : digest) {
        sb.append(LOWER_CHARS[(secretByte >> 4) & 15]);
        sb.append(LOWER_CHARS[secretByte & 15]);
    }
    System.out.println(sb.toString());//输出长度为64的字符串
}

更安全的写法

不管是MD5还是SHA算法,都是可以通过暴力破解的方式完成,只不过付出的代码很大。如果加密的内容很简单就存在泄露的风险。那又要怎么去防范呢?

为了追求更安全,有一种方式基本是没办法破解,那就是将MD5和SHA组合。将一段明文用MD5和SHA分别加密,得到两个密文串,然后从MD5中切分一部分,再从SHA中切分一部分,将切出的部分组合,形成最终密文串。即使使用暴力破解的方式碰巧比对成功,最终得到的结果也是和原明文不同。即使这里知道密文是MD5和SHA的组合产物,具体切分组合规则也是无法知道的,更别提如何去破解。

Source Code

码云(gitee):https://gitee.com/itcrud/itcrud-note/tree/master/itcrud-note-1-6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值