哈希算法总结

哈希算法可对任意一组输入数据进行计算,得到一个固定长度的输出摘要。哈希算法可以验证原始数据是否被篡改。

哈希算法的用途:

1.可用于校验下载文件

2.存储用户密码,直接将原始密码放入数据库中会有很大风险。

一、哈希碰撞

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

哈希碰撞无法避免,所以一个好的哈希算法必须要保证哈希碰撞概率低,不能猜测输出。

二、常用的哈希算法

哈希算法的输出长度越长,就越难产生碰撞。

1、MD5算法

MD5算法输出长度为16字节,128位。MD5算法只能进行加密不能进行解密。可通过比较加密后的结果,验证数据是否一致。

public class Demo01 {
public static void main(String[] args) {
	try {
		//创建基于md5算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("md5");
		//更新原始数据
		md5.update("天王盖地虎".getBytes());		
		//获取加密后的结果
		byte[] disgestBytes=md5.digest();
		
		System.out.println("加密后的结果(字节数组)"+Arrays.toString(disgestBytes));
		System.out.println("加密后的结果(十六进制字符串)"+HashTools.bytesToHex(disgestBytes));
		System.out.println("加密后的长度"+disgestBytes.length);
	} catch (NoSuchAlgorithmException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

MD5算法首先要通过MessageDigest类创建一个消息摘要对象,然后通过update()方法更新数据,数据必须为字节,也可以重复调用update()方法更新数据,只要顺序不变加密后的结果仍然不变。然后在调用digest()方法进行加密,该方法返回值为字节数组。所以加密结果要保存在字节数组中。

如果想要将加密结果以字符串输出,可以创建HashTools工具类,写一个bytesToHex()方法,将加密结果转换为字符串。"%02x"是指将字节转换为两位的16进制字符串。

   //将字节数组转换为16进制字符串
	public static String bytesToHex(byte[] bytes) {
		StringBuilder ret=new StringBuilder();
		//将字节数值转换为2位16进制字符串
		for(byte b:bytes) {
			ret.append(String.format("%02x", b));//format可将字节按照指定进制格式转换
		}
		return ret.toString();
	}

 例1:按照MD5算法对图片进行加密

MD5算法也可以对图片进行加密 ,图片也是由很多个字节组成。

public class Demo02 {
public static void main(String[] args) {
	try {
		//图片的原始字节内容
		byte[] bytes=Files.readAllBytes(Paths.get("D:\\y\\test\\2.jpg"));
		//创建基于md5算法的消息摘要对象
		MessageDigest md5=MessageDigest.getInstance("MD5");
		//更新原始数据
		md5.update(bytes);
		//获取加密后的结果
		byte[] disgestBytes=md5.digest(bytes);
		
		System.out.println("加密后的结果(字节数组)"+Arrays.toString(disgestBytes));
		System.out.println("加密后的结果(十六进制字符串)"+HashTools.bytesToHex(disgestBytes));
		System.out.println("加密后的长度"+disgestBytes.length);
		
	} catch (IOException | NoSuchAlgorithmException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

加密图片,则需通过Files.readAllBytes(paths.get(" "))方法先获取图片的原始字节内容。其他步骤一致。

2.通过随机加盐,解决彩虹表攻击问题

常用密码即便加密后,也容易反推出原始密码。

彩虹表就是通过预先计算好的常用口令和MD5对照表,黑课以此很容易反查到原始口令。也就是彩虹表攻击。

为解决彩虹表攻击,那么可以额外添加随机数,也就是加盐。

盐可以是随即数也可以是固定的。

public class Demo03 {
public static void main(String[] args) throws NoSuchAlgorithmException {
	//原始密码
	String password="wbjxxmy";
	//产生随机的盐值
	String salt=UUID.randomUUID().toString().substring(0,4);
	
    //MessageDigest md5=MessageDigest.getInstance("MD5");

	//基于SHA-1算法的消息摘要对象
	MessageDigest sha=MessageDigest.getInstance("SHA-1");
//	md5.update(password.getBytes());
	sha.update(password.getBytes());//原始密码
	sha.update(salt.getBytes());//盐值
	
//	String digestHex=HashTools.bytesToHex(md5.digest());
	//计算加密结果,SHA-1的输出结果为20个字节(40个字符)
	String digestHex=HashTools.bytesToHex(sha.digest());
	System.out.println(digestHex);
}
}

MD5和SHA-1算法都可加盐。

2、SHA算法

SHA算法是一个系列算法,也是一种哈希算法,常用的有SHA-1,SHA-256,SHA-512等,它们的区别只是输出长度不同。

1、SHA-1

SHA-1的输出是160bits,即20字节。

使用SHA-1和MD5算法完全一样,只需要将算法名称改为”SHA-1"即可。

//基于SHA-1算法的消息摘要对象
 MessageDigest sha=MessageDigest.getInstance("SHA-1");

2.SHA-256

SHA-256的输出是256bits,32字节。使用时只需要将算法名称改为”SHA-256"即可。

3.SHA-512

SHA-512的输出是512bits,64字节。使用时只需要将算法名称改为”SHA-512"即可。

哈希算法工具类

MD5算法和SHA-1算法在算法加密时有多个步骤是相同的,可将其封装在HashTools工具类中.

//Hash算法(消息摘要算法)工具类
public class HashTools {
	//消息摘要对象
	private static MessageDigest digest;
	//构造方法私有
	private HashTools() {}
	//将字节数组转换为16进制字符串
	public static String bytesToHex(byte[] bytes) {
		StringBuilder ret=new StringBuilder();
		//将字节数值转换为2位十进制字符串
		for(byte b:bytes) {
			ret.append(String.format("%02x", b));
		}
		return ret.toString();
	}
	
	//按照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);
		}
	//通过消息摘要对象处理加密内容
		private static String handler(String source) {
			digest.update(source.getBytes());
			byte[] bytes=digest.digest();
			String hash=bytesToHex(bytes);
			return hash;
		}	
}
public class Demo04 {
public static void main(String[] args) {
	try {
		//md5加密
		String md5=HashTools.digestByMD5("wbjxxmy");
		//SHA-1加密
		String sha1=HashTools.digestBySHA1("wbjxxmy");
				
		System.out.println("md5="+md5);
		System.out.println("sha-1="+sha1);
		
	} catch (NoSuchAlgorithmException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

3、RipeMD-160算法

RipeMD-160消息摘要算法是使用第三方开源库实现的。

它的输出长度是160bits,20字节。

RipeMD-160算法使用时要先注册bouncyCastleBouncyCastleProvider通知类,然后将提供的消息摘要算法注册至Security。然后再才能创建消息摘要对象。

public class Demo03 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		//注册bouncyCastleBouncyCastleProvider通知类
		//将提供的消息摘要算法注册至Security
		Security.addProvider(new BouncyCastleProvider());
		
		//创建基于ripeMd160算法的消息摘要对象
		MessageDigest ripeMd160=MessageDigest.getInstance("ripeMd160");
	    //更新原始数据
		ripeMd160.update("天王盖地虎".getBytes());		
		//获取加密后的结果
		byte[] disgestBytes=ripeMd160.digest();				
		//消息摘要的字节长度和内容
		System.out.println("加密后的结果(字节数组)"+Arrays.toString(disgestBytes));		
		System.out.println("加密后的长度"+disgestBytes.length);
		
		//16进制内容字符串
		String hex=new BigInteger(1,disgestBytes).toString(16);
		System.out.println("加密后的结果(字符串内容)"+Arrays.toString(disgestBytes));		
		System.out.println("加密后的结果(字符串长度)"+disgestBytes.length);		
	}

}

RipeMD160消息摘要算法实现首先要通过new BouncyCastleProvider()注册BouncyCastleProvider通知类,再调用Security.addProvider()将提供的消息摘要算法注册至Security。然后通过MessageDigest类创建基于ripeMd160算法的消息摘要对象。然后调用update()更新数据,调用digest()方法进行加密。

三、Hmac算法

Hmac算法是一种基于密钥的消息认证算法。Hmac算法总是和某种哈希算法配合使用。例如使用MD5算法,对应的是HmacMD5算法,相当于’加盐‘的MD5。

为了保证安全,我们不会自己指定key,而是通过Java标准库的KeyGenerator生成随机的key.HmacMD5的key是64字节。

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.使用秘钥进行加密
	//获取Mac加密算法对象
	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()); 
}
}

Hmac算法分为两步:1.产生秘钥 2.使用秘钥进行加密

产生密钥先通过KeyGenerator.getInstance("HmacMD5")获取HmacMD5秘钥生成器,再调用generateKey()生成秘钥。秘钥的长度是固定的,但每次生成的内容不同,提高了安全性。在生成秘钥后通过Mac.getInstance("HmacMD5")获取Mac加密算法对象,在调用mac.init(key)初始化秘钥.同样通过update()方法更新原始加密内容。不同的是Hmac算法调用doFinal()进行加密处理。加密结果同样是字节。

我们想要进行验证时就无法生成密钥了,而是要从字节数组或字符串恢复密钥。

public class Demo02 {
public static void main(String[] args) {
	//原始密码
	String password="wbjxxmy";
	
	//秘钥(字节数组)
    //byte[] keyBytes= {118, -124, 86, -65, -20, 45, 73, -117, -62, 107, 53, 27, -113, 20, 9, 3, -8, -66, -31, 108, 104, -16, -86, 100, -114, 103, -124, 100, 75, 53, -50, 37, -53, -94, -126, -18, -125, 8, 20, 115, -51, -11, -98, 93, 111, 18, 50, -18, -94, -76, -116, 87, -59, 112, -81, -11, -67, 64, 15, -107, -3, -7, 119, 33};
	//秘钥(字节串)
	String keystr="6db400f5489bf3591d5f573428a6b137";
	//用于保存秘钥:密钥长度为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);//转换为16进制byte值
	}
	
	//恢复密钥并加密
	try {
		//恢复密钥(字节数组)
		SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5");
		
		//创建Hmac加密算法对象
		Mac mac=Mac.getInstance("HmacMD5");
		mac.init(key);//初始化密钥
		mac.update(password.getBytes());

		String result=HashTools.bytesToHex(mac.doFinal());
		System.out.println(result);
	} catch (InvalidKeyException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (NoSuchAlgorithmException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
}
}

当密钥内容是字节数组时,我们要通过SecretKey key=new SecretKeySpec(keyBytes, "HmacMD5")来恢复密钥。参数一:字节数组 参数二:指定算法

当密钥是字符串时我们要先将其转换为字节数组,然后再进行恢复密钥。密钥恢复后才能进行加密。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值