哈希算法总结

目录

Hash算法概述

常用的哈希算法:

          HMAC算法 

Java标准库提供了一系列常用的哈希算法。但如果我们要用的某种算法,Java标准库没有提供怎么办?


 

Hash算法概述

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

哈希算法的主要特点:
相同的输入一定得到相同的输出,不同的输入大概率得到不同的输出

哈希算法的作用:

主要用于原始数据是否被篡改

Java字符串的hashCode()就是一个哈希算法

哈希碰撞:

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

例如:

"通话".hashCode(); 
// 0x11ff03
"重地".hashCode(); 
// 0x11ff03

        碰撞能不能避免?答案是不能。碰撞是一定会出现的,因为输出的字节长度是固定的,String的hashCode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
        碰撞不可怕,我们担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系到哈希算法的安全性。一个安全的哈希算法必须满足:
        碰撞概率低;
        不能猜测输出。
不能猜测输出是指:输入的任意一个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为例:

写一个将字节数组转换为十六进制的工具类: 

 

//图片加密
public class test6 {
	public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
        //图片的原始字节内容读取
		byte[] imgbyte=Files.readAllBytes(Paths.get("D://test//news.jpg"));
        //创建基于MD5算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("MD5");
        //原始字节内容
		md5.update(imgbyte);
		byte[] md5img=md5.digest();
        //获取加密摘要
		System.out.println("加密后:"+Arrays.toString(md5img));
		System.out.println("加密后:"+HashTools.bytesTohex(md5img));
		System.out.println("长度"+md5img.length);
	}
}

 运行结果:

 

哈希算法的另一个重要用途是存储用户口令。如果直接将用户的原始口令存放到数据库中,会产生极大的安全风险:
●数据库管理员能够看到用户明文口令;
●数据库数据一旦泄漏,黑客即可获取用户明文口令。

username

password

bob

123456789

alice

sdfsdfsdf

tim

justdoit

不存储用户的原始口令,那么如何对用户进行认证?方法是存储用户口令的哈希,例如,MD5。在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。
因此,数据库存储用户名和口令的表内容应该像下面这样:

username

password

bob

25f9e794323b453885f5181f1b624d0b

alice

73a90acaae2b1ccc0e969709665bc62f

tim

19f9f30bd097d4c066d758fb01b75032

这样一来,数据库管理员看不到用户的原始口令。即使数据库泄漏,黑客也无法拿到用户的原始口令。想要拿到用户的原始口令,必须用暴力穷举的方法,一个口令一个口令地试,直到某个口令计算的MD5恰好等于指定值。
使用哈希口令时,还要注意防止彩虹表攻击。
什么是彩虹表呢?上面讲到了,如果只拿到MD5,从MD5反推明文口令,只能使用暴力穷举的方法。然而黑客并不笨,暴力穷举会消耗大量的算力和时间。但是,如果有一个预先计算好的常用口令和它们的MD5的对照表,这个表就是彩虹表。如果用户使用了常用口令,黑客从MD5一下就能反查到原始口令:

常用口令

MD5

hello123

f30aa7a662c728b7407c54ae6bfd27d1

12345678

25d55ad283aa400af464c76d713c07ad

passw0rd

bed128365216c019988915ed3add75fb

19700101

570da6d5277a646f6552b8832012f5dc

wbjxxmy

11d7a82f45f6a176fd9d5c100ccab40a

这就是为什么不要使用常用密码,以及不要使用生日作为密码的原因。
当然,我们也可以采取特殊措施来抵御彩虹表攻击:对每个口令额外添加随机数,这个方法称之为加盐(salt):
digest = md5(salt + inputPassword)
经过加盐处理的数据库表,内容如下:

username

salt

password

bob

H1r0a

a5022319ff4c56955e22a74abcc2c210

alice

7$p2w

e5de688c99e961ed6e560b972dab8b6a

tim

z5Sk9

1eee304b92dc0d105904e7ab58fd2f64

 

package com.apesource.yuan3;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class test7 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
        //原始密码
		String Password="854987526";
        //创建基于SHA-1算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("SHA-1");
		//盐值
		String yan=UUID.randomUUID().toString().substring(0,4);
		md5.update(Password.getBytes());//原始密码
		md5.update(yan.getBytes());//盐值
        //计算加密结果
		byte[] pass= md5.digest();
		System.out.println("加密后:"+HashTools.bytesTohex(pass));
	}
}

SHA-1
        SHA-1也是一种哈希算法,它的输出是160 bits,即20字节。SHA-1是由美国国家安全局开发的,SHA算法实际上是一个系列,包括SHA-0(已废弃)、SHA-1、SHA-256、SHA-512等。
在Java中使用SHA-1,和MD5完全一样,只需要把算法名称改为"SHA-1",因此,我们可以写一个工具类:

package com.apesource.yuan3;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HashTools {
    //消息摘要对象
	private static MessageDigest digst;
	//构造方法私有
	private HashTools() {
		
	}
    //按照MD5进行消息摘要计算(哈希算法)
	public static String digestByMD5(String source) throws NoSuchAlgorithmException {
		digst=MessageDigest.getInstance("MD5");
		return handler(source);
	}
    //按照SHA-1进行消息摘要计算(哈希算法)
	public static String digestBySHA1(String source) throws NoSuchAlgorithmException {
		digst=MessageDigest.getInstance("SHA-1");
		return handler(source);
	}
    //按照SHA-256进行消息摘要计算(哈希算法)
	public static String digestBySHA256(String source) throws NoSuchAlgorithmException {
		digst=MessageDigest.getInstance("SHA-256");
		return handler(source);
	}
    //按照SHA-512进行消息摘要计算(哈希算法)
	public static String digestBySHA512(String source) throws NoSuchAlgorithmException {
		digst=MessageDigest.getInstance("SHA-512");
		return handler(source);
	}
    //通过消息摘要对象,处理加密内容
	private static String handler(String source) {
		digst.update(source.getBytes());
		byte[] bytes = digst.digest();
		String hash=bytesTohex(bytes);
		return hash;
	}
    //将字节数组转换为16进制字符串
	public static String bytesTohex(byte[] bytes) {
		StringBuilder ret=new StringBuilder();
		for(byte e:bytes)
		{
			ret.append(String.format("%02x", e));
		}
		return ret.toString();
		}
}


 使用实例:

public static void main(String[] args) throws NoSuchAlgorithmException {
		String password="852654455";
		String md5=HashTools.digestByMD5(password);
		String sha1=HashTools.digestBySHA1(password);
		String sha256=HashTools.digestBySHA256(password);
		String sha512=HashTools.digestBySHA512(password);
		
		System.out.println("md5"+md5);
		System.out.println("sha1:"+sha1);
		System.out.println("sha256:"+sha256);
		System.out.println("sha512:"+sha512);
	}

HMAC算法 

 

        HMAC算法是一种基于哈希算法的消息认证算法,它需要使用一个哈希函数来计算消息的摘要。它通常是在哈希函数的基础上进行设计和实现的。

        具体来说,HMAC算法先将密钥按照一定规则处理后,与消息一起输入到哈希函数中,生成消息的摘要。这个过程需要使用到哈希函数的计算能力,但同时也需要进行一些额外的步骤,如密钥的处理和拼接等。

        HMAC算法它是一个运用哈希算法的特定应用,通过在哈希函数的基础上加入密钥和其他额外操作来实现的。

public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
		String pass="wbjxxmynhmyzgq";
		//产生密钥
		//获取HmacMD5密钥生成器
		KeyGenerator keyGen=KeyGenerator.getInstance("HmacMD5");
		//生成密钥
		SecretKey key = keyGen.generateKey();
		System.out.println();
		System.out.println("密钥:"+Arrays.toString(key.getEncoded()));
		System.out.println("密钥长度:"+key.getEncoded().length);
		System.out.println("密钥:"+HashTools.bytesTohex(key.getEncoded()));
		//使用密钥进行加密
		//获取Hmac加密算法对象
		Mac mac=Mac.getInstance("HmacMD5");
		mac.init(key);//初始化密钥
		mac.update(pass.getBytes());//更新原始加密内容
		byte[] bytes=mac.doFinal();//加密处理,或取加密结果
		String result=HashTools.bytesTohex(bytes);//加密结果处理成16进制字符串
		System.out.println("加密结果16进制字符串"+result);
		System.out.println("加密结果(字符串长度16字节)"+bytes.length);
		System.out.println("加密结果(字符长度32字符)"+result.length());
		
		
	}

Java标准库提供了一系列常用的哈希算法。但如果我们要用的某种算法,Java标准库没有提供怎么办?

方法一:自己写一个,难度很大;
方法二:找一个现成的第三方库,直接使用。
BouncyCastle就是一个提供了很多哈希算法和加密算法的第三方开源库。它提供了Java标准库没有的一些算法,例如,RipeMD160哈希算法。 

       首先,我们必须把BouncyCastle提供的bcprov-jdk15on-1.70.jar添加至classpath。这个jar包官网连接如下:官方网站icon-default.png?t=N658https://www.bouncycastle.org/latest_releases.html        其次,Java标准库的java.security包提供了一种标准机制,允许第三方提供商无缝接入。我们要使用BouncyCastle提供的RipeMD160算法,需要先把BouncyCastle注册一下:

public static void main(String[] args) throws NoSuchAlgorithmException {
		//注册BouncyCastleBouncyCa通知类
		//将提供的消息摘要算法注册至Security
		Security.addProvider(new BouncyCastleProvider());
         获取RipeMD160算法的"消息摘要对象"(加密对象)
		MessageDigest ripeMd160=MessageDigest.getInstance("RipeMD160");
        //更新原始数据
		ripeMd160.update("wbjxxmy".getBytes());
        //获取消息摘要(加密)
		byte[] result=ripeMd160.digest();
        //输出
		System.out.println(HashTools.bytesTohex(result));
		System.out.println(result.length);
		System.out.println(HashTools.bytesTohex(result).length());
	}

BouncyCastle是一个开源的第三方算法提供商,BouncyCastle提供了很多Java标准库没有提供的哈希算法和加密算法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猿究◎小飞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值