【项目小知识】Java中的MD5加密——底层由哈希算法来实现

我是灼灼,一只初学Java的大一金渐层。
向往余秀华和狄兰·托马斯的疯狂,时常沉溺于将情感以诗相寄;追逐过王尔德、王小波的文字,后陷于毛姆和斯蒂芬·金不可自拔;热爱文学的浪潮,白日梦到底却总在现实里清醒;艳羡平静又极度渴盼奔跑的力量。
欢迎与我交流鸭· QQ:1517526827;
个人博客:https://blog.csdn.net/weixin_52777510?spm=1001.2101.3001.5343

哈希算法

MD5加密的底层是由哈希算法来实现的
内容来自廖雪峰官方Java教程

  • 哈希算法(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

碰撞不能避免,碰撞是一定会出现的;
因为输出的字节长度是固定的,String的hashCode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合必然会产生碰撞。

碰撞不可怕,应该担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系到哈希算法的安全性

扫描二维码关注公众号,回复: 12683867 查看本文章

一个安全的哈希算法必须满足:

  • 碰撞概率低;
  • 不能猜测输出。
    这是指,输入的任意一个bit的变化会造成输出完全不同,这样就很难从输出反推输入(只能依靠暴力穷举)。

安全的哈希算法从输出是看不出任何规律的;

常用的哈希算法有:

算法 输出长度(位) 输出长度(字节)
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));
    }
}

Md5加密四步走!

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

哈希算法的用途

因为相同的输入永远会得到相同的输出,因此,如果输入被修改了,得到的输出就会不同。

在网站上下载软件的时候,经常可以看到下载页显示的哈希;

Q:如何判断下载到本地的软件是原始的、未经篡改的文件?
A:只需要计算一下本地文件的哈希值,再与官网公开的哈希值对比,如果相同,说明文件下载正确,否则,**说明文件已被篡改**

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

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

Q:不存储用户的**原始口令**(例如原始密码123456),应如何对用户进行认证?
A:方法是存储用户口令的哈希,例如MD5。

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

因此,数据库存储用户名和口令的表内容中,口令即密码存储的是Md5加密后的值;

这样,数据库管理员看不到用户的原始口令。即使数据库泄漏,黑客也无法拿到用户的原始口令。想要拿到用户的原始口令,必须用暴力穷举的方法,一个口令一个口令地试,直到某个口令计算的MD5恰好等于指定值。

彩虹表攻击

使用哈希口令时,还要注意防止彩虹表攻击。

什么是彩虹表?如果只拿到MD5,从MD5反推明文口令,只能使用暴力穷举的方法。

如果有一个预先计算好的常用口令和它们的MD5的对照表,嘿嘿…

这个表就是彩虹表。如果用户使用了常用口令,黑客从MD5一下就能反查到原始口令;

即使用户使用了常用口令,也可以采取措施来抵御彩虹表攻击,方法是对每个口令额外添加随机数(扰乱加密后的口令),这个方法称之为加盐(salt)——

digest = md5(salt+inputPassword)

加盐的目的在于使黑客的彩虹表失效,即使用户使用常用口令,也无法从MD5反推原始口令。

SHA-1

SHA-1也是一种哈希算法,它的输出是160 bits,即20字节。
SHA-1是由美国国家安全局开发的,SHA算法实际上是一个系列,包括SHA-0(已废弃)、SHA-1、SHA-256、SHA-512等。

在Java中使用SHA-1,和MD5完全一样,只需要把算法名称改为"SHA-1":

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

类似的,计算SHA-256,需要传入名称"SHA-256",计算SHA-512,需要传入名称"SHA-512"。Java标准库支持的所有哈希算法可以在这儿查到。

注意:MD5因为输出长度较短,短时间内破解是可能的,目前已经不推荐使用。

总结

哈希算法可用于验证数据完整性,具有防篡改检测的功能;

常用的哈希算法有MD5、SHA-1等;

用哈希存储口令时要考虑彩虹表攻击。

以上就是哈希算法的全部内容啦!

再见~
在这里插入图片描述
如果对你有帮助的话不要忘记一键三连噢~
谢谢鸭~

初次编写于2021/2/18日;

猜你喜欢

转载自blog.csdn.net/weixin_52777510/article/details/113854950