Java哈希算法总结

目录

一.编码算法

1.URL编码

2,Base64编码

二,哈希算法

1,概述

2,特点

3,哈希碰撞

4,常见哈希算法

三,Hmac算法

四,BouncyCastle

1,概述

2,用法


一.编码算法

在学习哈希算法之前我们先了解什么是编码

ASCII码就是一种编码,例如A的编码是16进制的0x41。因为ASCII码只能有127个字符:A~Z,a~z,0~9以及-,_,.,*。若相对更多文字进行编码就需要占用两个字节的Unicode 或者 三个字节的UTF-8所以简单的编码是直接给每个字符指定一个若干字节表示的整数,复杂一点的编码就需要根据一个已有的编码推算出来,就出现了编码算法。


1.URL编码

URL 编码是浏览器发送数据给服务器时使用的编码,它通常附加在 URL 的参数 部分,例如: https://www.baidu.com/s?wd=%E4%B8%AD%E6%96%87

Java标准库提供了一个URLEncoder类用来对任意字符串进行URL编码。

我们来举个例子:

// URL编码
public class Demo01 {
	public static void main(String[] args) {
		// URLEncoder类:进行URL编码操作
		// URLDecoder类:进行URL解码操作
		
		// 编码处理
		try {
			String encoder = URLEncoder.encode("编码算法", "utf-8");
			System.out.println(encoder);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// 解码处理
		try {
			String decode = URLDecoder.decode("%E7%BC%96%E7%A0%81%E7%AE%97%E6%B3%95", "utf-8");
			System.out.println(decode);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

编码:

根据URLEncoder这个类提供的encode方法,第一个参数是想要编码的字符串,第二个参数是编码类型。

解码:

根据URLDecoder这个类提供的decode方法,同上。


2,Base64编码

URL 编码是对字符进行编码,表示成 %xx 的形式,而 Base64 编码是对二进制数据进行 编码,表示成文本格式。

Base64 编码可以把任意长度的二进制数据变为纯文本,并且纯文本内容中且只包含指定 字符内容: A ~ Z 、 a ~ z 、 0 ~ 9 、 + 、 / 、 = 。它的原理是把 3 字节的二进制 数据按 6bit 一组,用 4 个int整数表示,然后查表,把 int 整数用索引对应到字符, 得到编码后的字符串。

注:二进制按6位分一组转换成十六进制 

 

public class Demo002 {

	public static void main(String[] args) {
		byte[] bytes = new byte[] {(byte)0xe4,(byte)0xb8,(byte)0xad};
		//编码
		String b64Encode = Base64.getEncoder().encodeToString(bytes);
		System.out.println(b64Encode);
		//解码
		byte[] b64Decode = Base64.getDecoder().decode("5Lit");
		String out = Arrays.toString(b64Decode);
		System.out.println(out);
		
		// 基于Base64解码
		byte[] getDecoderRet = Base64.getDecoder().decode("5Lit");
		for(byte b : getDecoderRet) {
			System.out.println(Integer.toHexString(b));
		}
	}
}

Base64 编码的目的是把二进制数据变成文本格式,这样在很多文本中就可以处理二进制数据。例如,电 子邮件协议就是文本协议,如果要在电子邮件中添加一个二进制文件,就可以用 Base64 编码,然后以文本的形式传送。

Base64 编码的缺点是传输效率会降低,因为它把原始数据的长度增加了1/3。和 URL 编码一样, Base64 编码是一种编码算法,不是加密算法。


二,哈希算法

1,概述

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

2,特点

相同的输入一定得到相同的输出; 不同的输入大概率得到不同的输出。 所以,哈希算法的目的:为了验证原始数据是否被篡改。

3,哈希碰撞

哈希碰撞是指两个不同的输入得到两个不同的输出。这是不可避免的。

4,常见哈希算法

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

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


 MD5算法

public class Demo01 {

	public static void main(String[] args) throws NoSuchAlgorithmException {
		// 创建基于MD5算法的消息摘要对象
		MessageDigest md = MessageDigest.getInstance("MD5");
		
		// 更新原始数据
		md.update("天王该独户".getBytes());
		md.update("马元两米五".getBytes());
		
		// 获取加密后的结果
		byte[] digestByte = md.digest();
		System.out.println("加密后的结果(字节数组)"+Arrays.toString(digestByte));
		System.out.println("加密后的结果(16进制字符串)"+HashTools.bytesToHex(digestByte));
		System.out.println(digestByte.length);
		
		
	}
}

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

// 按照MD5算法对图片进行加密
public class Demo02 {
	public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
		// 图片原始字节内容
		byte[] imgbytes = Files.readAllBytes(Paths.get("C:\\Users\\lenovo\\Desktop\\cbx\\douban\\xuan\\123.jpg"));
		
		//创建基于MD5的算法的消息摘要对象
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		
		// 原始字节内容
		md5.update(imgbytes);
		
		//获取加密摘要
		byte[] digestByte = md5.digest();
		System.out.println("加密后的结果(字节数组)"+Arrays.toString(digestByte));
		System.out.println("加密后的结果(16进制字符串)"+HashTools.bytesToHex(digestByte));
		System.out.println(digestByte.length);
	}
}

 上面两个结果可以看出加密后的字节长度固定为16

虽然说是加密了,因为相同的输入永远会得到相同的输出,因此,如果输入被修改了,得到的输出就会不同

有些坏蛋他根据用户常用的密码信息做成彩虹表,当然,我们也可以采取特殊措施来抵御彩虹表攻击:对每个口令额外添加随机数,这个方法称之为加盐。如下:


SHA-1算法

public class salt {

	public static void main(String[] args) throws NoSuchAlgorithmException {
		MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
		
		String salt = UUID.randomUUID().toString().substring(0,4);
		sha1.update("马元两米五".getBytes());
		sha1.update(salt.getBytes());
		
		byte[] dg = sha1.digest();
		System.out.println(HashTools.bytesToHex(dg));
	}
}

当然每次运行结果都不一样。黑客不知道我们每次加的盐也就是随机数是什么,所以也就没办法盗取密码了。

上面我们可以看出SHA-1算法与MD5算法有许多相似的地方,我们把他们封装到一个类中:

public class HashTools {

	private static MessageDigest digest;
	
	private HashTools() {}
	
	public static String digestBymd5(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("MD5");
		return handler(source);
	}
	
	public static String digestBysha1(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("SHA-1");
		return handler(source);
	}
	
	public static String handler(String source) {
		digest.update(source.getBytes());
		byte[] bytes = digest.digest();
		String hash = bytesToHex(bytes);
		return hash;
	}
	
	
	public static String bytesToHex(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for(byte b : bytes) {
			sb.append(String.format("%02x", b));
		}
		return sb.toString();
	}	
}

对外直接调用对应的方法即可: 

public class Demo03 {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		String md5 = HashTools.digestBymd5("马元两米五");
		String sha1= HashTools.digestBysha1("马元两米五");
		System.out.println(md5);
		System.out.println(sha1);
	}
}

 


三,Hmac算法

Hmac 算法就是一种基于密钥的消息认证码算法,它的全称是 Hash-based Message Au thentication Code ,是一种更安全的消息摘要算法。

HmacMD5 可以看作带有一个安全的 key 的 MD5 。使用 HmacMD5 而不是用 MD5 加 salt ,

优点:

HmacMD5 使用的 key 长度是 64 字节,更安全;

Hmac 是标准算法,同样适用于 SHA-1 等其他哈希算法;

Hmac 输出和原有的哈希算法长度一致。

// Hmac密钥
public class Demo01 {

	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
		// 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("马元两米五".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("加密结果"+result.length());	
	}
}

有了 Hmac 计算的哈希和 SecretKey ,我们想要验证怎么办?这时, SecretKey 不能从 KeyGenerator 生成,而是从一个 byte[] 数组恢复(通过密钥字符串或者密钥字节数组):

public class Demo02 {

	public static void main(String[] args) {
		String password = "马元两米五";
		
		// 密钥字节数组
		//byte[] bytes =  {85, -56, -112, 81, 91, -98, -45, 35, -45, -70, -105, 32, -39, -87, 95, -35, 125, 125, 52, 24, -39, -122, -21, -113, -111, 2, 38, -65, 1, 105, 123, -85, -66, 34, 10, 41, 122, 24, 105, 98, 29, 107, -99, -106, 72, 57, 79, 22, -76, 109, 115, -86, 98, 25, 5, -56, -27, -69, 76, -70, -112, 69, 48, 127};
		
		// 密钥(字符串)
		String keyStr = "55c890515b9ed323d3ba9720d9a95fdd7d7d3418d986eb8f910226bf01697babbe220a297a1869621d6b9d9648394f16b46d73aa621905c8e5bb4cba9045307f";
		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);
		}
		
		
		try {
			//恢复密钥
			SecretKey key = new SecretKeySpec(keybytes, "HmacMD5");
			
			//加密
			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) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		}
	}
}

 

结果与上一次结果相同。

四,BouncyCastle

如果我们要用的算法在java标准库中没有的话,我们可以使用第三方库。

1,概述

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

2,用法

添加jar包:

bcprov-jdk15on-1.70.jar

使用之前需要注册BouncyCastle(代码如下):

public class Demo01 {

	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
		// 注册BouncyCastleProvider通知类
		// 将提供的消息摘要算法注册至Security
		Security.addProvider(new BouncyCastleProvider());
		
		// 获取ripeMD160算法的“消息摘要对象”(加密对象)
		MessageDigest ripeMD160 = MessageDigest.getInstance("RipeMD160");
		
		// 更新原始数据
		ripeMD160.update("马元两米五".getBytes()); 
		
		byte[] bytes = ripeMD160.digest();
		String result = HashTools.bytesToHex(bytes);
		System.out.println("加密结果16进制字符串:"+result);
		System.out.println("加密结果(字节长度16字节)"+bytes.length);
		System.out.println("加密结果"+result.length());
	}
}

 

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值