哈希算法总结

一、哈希算法概述

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

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

1、相同的输入一定得到相同的输出;

2、不同的输入大概率得到不同的输出。

所以,哈希算法的目的:为了验证原始数据是否被篡改。

哈希算法的用途:

1、校验下载文件

我们只需要计算一下本地文件的哈希值,再与官网公开的哈希值对比,如果相同,说明文件下载正确,否则,说明文件已被篡改。

2、存储用户密码

如果直接将用户的原始口令存放在数据库中,会产生安全风险。利用哈希算法存储用户口令的哈希,在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。使用哈希口令时,还要注意防止彩虹表攻击,可以采用加盐(salt)的方式抵御彩虹表攻击:对每个口令额外添加随机数。

二、常用的哈希算法

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

常用的哈希算法有:

算法输出长度(位)输出长度(字节)
MD5128 bits16 bytes
SHA-1160 bits20 bytes
RipeMD-160160 bits20 bytes
SHA-256256 bits32 bytes
SHA-512512 bits64 bytes

2.1  MD5算法

首先创建基于MD5算法的消息摘要对象,然后调用update()输入数据,输入结束后,调用digest()方法获取byte[]数组表示的摘要,最后调用工具类中的bytesToHex()方法将字节数组转换为十六进制字符串。

//Hash算法(消息摘要算法)工具类
public class HashTools {
    //构造方法为私有
	private HashTools() {}
    //将字节数组转换为16进制字符串
	public static String bytesToHex(byte[] bytes) {
		StringBuilder ret=new StringBuilder();
		for(byte b:bytes) {
			//将字节值转换为2位十六进制字符串
			ret.append(String.format("%02x", b));
		}
		return ret.toString();
	}
}

 原始内容相同,计算结果一定相同。此案例中输入的顺序相同,加密后的结果也相同,当输入顺序不同时,加密后的结果不同。 MD5算法固定输出长度为16个字节。

//MD5加密
public class Demo01 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		//创建基于MD5算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("MD5");
		//更新原始数据
		md5.update("天王盖地虎小鸡炖蘑菇".getBytes());
		//获取加密后的结果
		byte[] digestBytes=md5.digest();
		System.out.println("加密后的结果(字节数组):"+Arrays.toString(digestBytes));
		System.out.println("加密后的结果(16进制字符串):"+HashTools.bytesToHex(digestBytes));
		System.out.println("加密后的长度:"+digestBytes.length);
		
		//原始内容相同,计算结果一定相同
		MessageDigest Messmd5=MessageDigest.getInstance("MD5");
		//更新原始数据
		Messmd5.update("天王盖地虎".getBytes());
		Messmd5.update("小鸡炖蘑菇".getBytes());
		//获取加密后的结果
		byte[] digestBytes1=Messmd5.digest();
		System.out.println("加密后的结果(字节数组):"+Arrays.toString(digestBytes1));
		System.out.println("加密后的结果(16进制字符串):"+HashTools.bytesToHex(digestBytes1));
		System.out.println("加密后的长度:"+digestBytes1.length);

	}

}

 MD5算法也可以对图片进行加密,加密过程与上面对字符串的加密过程相似。

public class Demo03 {
	public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
		//图片的原始字节内容
		byte[] imageBuf=Files.readAllBytes(Paths.get("D:\\test\\20230708\\bg1.jpg"));
		//创建基于MD5算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("MD5");
		//原始字节内容(图片)
		md5.update(imageBuf);
		//获取加密后的结果
		byte[] digestBytes=md5.digest();
		System.out.println("加密后的结果(字节数组):"+Arrays.toString(digestBytes));
		System.out.println("加密后的结果(16进制字符串):"+HashTools.bytesToHex(digestBytes));
		System.out.println("加密后的长度:"+digestBytes.length); //MD5算法固定输出长度为16个字节

	}

}

2.2  SHA-1算法

最基础的加密过程就如上述代码展示的结果,在这个过程中,如果想要自己的加密更加安全,可以采取措施来抵御彩虹表的攻击,可以使用加盐(salt)的方法。不改变基本的加密过程步骤。

以SHA-1加盐示例:

public class Demo04 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		//原始密码
		String password="wbjxxmy";
		//产生随机的盐值
		String salt=UUID.randomUUID().toString().substring(0,4);
		//创建基于SHA-1算法的消息摘要对象
		MessageDigest sha1=MessageDigest.getInstance("SHA-1");
		sha1.update(password.getBytes());//原始密码
		sha1.update(salt.getBytes());//盐值
		//计算加密结果,SHA-1的输出结果为20个字节(40个字符)
		String digestHex=HashTools.bytesToHex(sha1.digest());
		System.out.println(digestHex);	
	}

}

由上述代码可以看出,加盐实际上就是产生一个随机字符,来解决加密结果的不确定性和安全性问题的。

2.3  SHA-256算法和SHA-512算法

 在Java中使用SHA-1、SHA-256和SHA-512,和MD5完全一样,只需要把算法名称改为对应要使用的算法名称即可。

我们可以创建一个Hash算法工具类,该工具类的作用是定义常用哈希算法进行消息摘要计算及处理加密内容的方法,在后续使用哈希算法时直接调用该类中的方法即可,避免代码重复。

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

}
public class Demo05 {
	public static void main(String[] args) {
		try {
			//md5加密
			String md5=HashTools.digestByMD5("wbjxxmy");
			//SHA-1加密
			String sha1=HashTools.digestBySHA1("wbjxxmy");
			//SHA-256加密
			String sha256=HashTools.digestBySHA256("wbjxxmy");
			//SHA-512加密
			String sha512=HashTools.digestBySHA512("wbjxxmy");
			System.out.println("md5="+md5);
			System.out.println("sha1="+sha1);
			System.out.println("sha256="+sha256);
			System.out.println("sha512="+sha512);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

}

2.4  Hmac算法

Hmac算法就是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。总是和某种哈希算法配合起来使用,本质上是把key密钥混入摘要的算法。验证此哈希时,除了原始的输入数据,还要提供key。

使用HmacMD5为例:

先获取HmacMD密钥生成器,然后通过KeyGenerator创建SecretKey实例,生成密钥,再获取Hmac机密算法对象,调用Mac的init()方法初始化密钥,然后更新原始加密内容,最后调用Mac的doFinal()方法进行加密处理,并获取加密结果,将加密结果处理成十六进制字符串。

//Hmac算法
public class Demo01 {
	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
		String password="wbjxxmy";
		//1.产生密钥
		//获取HmacMD5密钥生成器
		KeyGenerator keyGen=KeyGenerator.getInstance("HmacMD5");
		//生成密钥
		SecretKey key=keyGen.generateKey();
		System.out.println("密钥:"+Arrays.toString(key.getEncoded()));
		System.out.println("密钥长度(64字节):"+key.getEncoded().length);
		System.out.println("密钥:"+HashTools.bytesToHex(key.getEncoded()));
		//2.使用密钥进行加密
		//获取Hmac加密算法对象
		Mac mac=Mac.getInstance("HmacMD5");
		mac.init(key);//初始化密钥
		mac.update(password.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());
		
	}

}

 使用密钥的字节数组恢复密钥。恢复密钥的语句为SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5")。

public class Demo02 {
	public static void main(String[] args) {
		//原始密码
		String password="wbjxxmy";
		//密钥(字节数组)
		byte[] keyBytes= {-20, 110, 2, -67, -45, -128, 121, -30, -49, 45, -111, -88, 70, -116, 105, -25, 79, -34, -48, -13, 111, 32, -108, 70, 101, 21, 96, 98, -38, 9, 27, -80, -40, -110, 125, 125, -64, -15, 122, 112, -65, -35, 54, 91, 65, 62, -60, -4, 116, 111, -5, 25, -73, -92, 54, -121, -67, -120, 79, -48, -100, 91, 7, 17};
		//恢复密钥(字节数组)
		SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5");
		//创建Hmac加密算法对象
		try {
			Mac mac=Mac.getInstance("HmacMD5");
			mac.init(key);//初始化密钥
			mac.update(password.getBytes());
			String result=HashTools.bytesToHex(mac.doFinal());
			System.out.println(result);
			//9fb71485409c216aa8ca5a309747fc8b
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		}
		
	}
	

}

 使用密钥的字符串恢复密钥。for循环中将密钥的字符串两位两位的取,将取到的十六进制字符转换成十进制数,并且保存在数组中,最后使用SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5")语句恢复密钥。

public class Demo03 {
	public static void main(String[] args) {
		//原始密码
				String password="wbjxxmy";
				//密钥(字符串)
				String keyStr="ec6e02bdd38079e2cf2d91a8468c69e74fded0f36f20944665156062da091bb0d8927d7dc0f17a70bfdd365b413ec4fc746ffb19b7a43687bd884fd09c5b0711";
				//用于保存密钥:密钥长度为64字节
				byte[] keyBytes=new byte[64];
				for(int i=0,k=0;i<keyStr.length();i+=2,k++) {
					String s=keyStr.substring(i,i+2);
					keyBytes[k]=(byte)Integer.parseInt(s,16);
				}
				//恢复密钥(字节数组)
				SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5");
				//创建Hmac加密算法对象
				try {
					Mac mac=Mac.getInstance("HmacMD5");
					mac.init(key);//初始化密钥
					mac.update(password.getBytes());
					String result=HashTools.bytesToHex(mac.doFinal());
					System.out.println(result);
					//9fb71485409c216aa8ca5a309747fc8b
				} catch (NoSuchAlgorithmException e) {
					e.printStackTrace();
				} catch (InvalidKeyException e) {
					e.printStackTrace();
				}
	}

}

2.5 RipeMD-160算法

RIPEMD-160是一种基于Merkle-Damgård结构的加密哈希函数,它是比特币标准之一。RIPEMD-160是RIPEMD算法的增强版本,RIPEMD-160算法可以产生出160位的的哈希摘要。

由于Java标准库中没有提供RIPEMD-160算法,所以我们需要导入BouncyCastle提供的bcprov-jdk15on-1.70.jar包。

首先要注册BouncyCastleProvider通知类,将提供的消息摘要算法注册至Security,该算法后续加密过程与前面的算法一致。

示例代码:

//使用第三方开源库提供的RipeMD160消息摘要算法实现
public class Demo04 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		//注册BouncyCastleProvider通知类
		//将提供的消息摘要算法注册至Security
		Security.addProvider(new BouncyCastleProvider());
		//获取RipeMD160算法的"消息摘要对象"(加密对象)
		MessageDigest ripeMd160=MessageDigest.getInstance("RipeMD160");
		//更新原始数据
		ripeMd160.update("wbjxxmy".getBytes());
		//获取消息摘要(加密)
		byte[] result=ripeMd160.digest();
		//消息摘要的字节长度和内容
		System.out.println("加密结果(字节长度):"+result.length);//160位=20字节
		System.out.println("加密结果(字节内容):"+Arrays.toString(result));
		//16进制内容字符串
		String hex=new BigInteger(1,result).toString(16);
		System.out.println("加密结果(字符串长度):"+hex.length());//20字节=40个字符
		System.out.println("加密结果(字符串内容):"+hex);
	}

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
哈希表是一种常用的数据结构,它通过哈希函数将键映射到存储位置,以实现高效的数据查找和插入操作。哈希函数是一种提取数据特征的算法,根据不同的数据形式和场景,可以选择不同的哈希算法。常见的哈希算法包括MD5等。\[1\] 在哈希表中,哈希函数的优劣直接影响到哈希表的查找效率。优秀的哈希函数可以减少冲突的发生,提高查找效率。哈希函数的设计方法有多种,其中常见的包括直接寻址法、除留余数法、平方取中法等。不同的哈希函数适用于不同的数据类型和规律。\[3\] 哈希冲突是指不同的键经过哈希函数计算后得到相同的哈希值,导致数据存储位置冲突的情况。为了解决哈希冲突,常用的方法有开放寻址法和链地址法。开放寻址法是指当发生冲突时,通过一定的规则在哈希表中寻找下一个可用的位置来存储数据。链地址法是指在哈希表的每个位置上维护一个链表,将哈希值相同的键值对存储在同一个链表中。\[2\] 总结来说,哈希表是一种通过哈希函数将键映射到存储位置的数据结构,常用的哈希算法有多种,哈希函数的设计方法也有多种,而哈希冲突的处理方法包括开放寻址法和链地址法。这些算法和数据结构的选择取决于具体的应用场景和需求。 #### 引用[.reference_title] - *1* [详解哈希数据结构,手写哈希表](https://blog.csdn.net/CRMEB/article/details/120820682)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [数据结构之哈希表以及常用哈希的算法表达(含全部代码)](https://blog.csdn.net/weixin_53050357/article/details/126666617)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [哈希表-数据结构(C语言)](https://blog.csdn.net/weixin_44681349/article/details/124782035)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值