哈希算法及加密实现

文章目录

  • 前言
  • 一、哈希算法是什么?
    • 哈希算法特点
    • 哈希碰撞
    • 常用的哈希算法
  • 二、哈希算法对密码加密的实现
    • 1.MD5加密
    • 2.SHA-1,SHA-256,SHA-512加密
    • 3.ripeMd160加密
    • 4.加盐
  • 总结


前言

哈希算法被广泛运用在各种途径中,本文章将带你初步认识哈希算法及它的加密的实现操作效果。


一、哈希算法是什么?

哈希算法又称摘要算法(Disgest),一般指SHA家族,它是安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。

哈希算法特点

  • 相同的输入一定得到相同的输出
  • 不同的输入大概率得到不同的输出
    因此,哈希算法的目的是验证原始数据是否被篡改
    我们可以调用java的hashCode方法验证
System.out.println("Hello Java".hashCode());// 387417328
System.out.println("Hello java".hashCode());// 388370640
System.out.println("Hello,bob".hashCode());//-1095500357

哈希碰撞

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

	String s1 = "通话";
	String s2 = "重地";
	System.out.println(s1.hashCode());// 1179395
	System.out.println(s2.hashCode());// 1179395

哈希碰撞是不能避免的,这是因为输出的字节长度是固定的,但输入的字节长度不固定,有无数种输入,所以哈希算法是把无数的输入集合映射到一个有限的输出集合里,必然会产生碰撞。
既然不能避免碰撞,我们就要想办法降低碰撞的概率,提高哈希算法的安全性,所以一个安全的哈希算法必须满足:

  • 碰撞概率低
  • 不能被预测输出

常用的哈希算法

算法 输出长度(位) 输出长度(字节)
MD5 128bits 16bytes
SHA-1 160bits 20bytes
RipeMD-160 160bits 20bytes
SHA-256 256bits 32bytes
SHA-512 512bits 64bytes

二、哈希算法对密码加密的实现

为什么要对密码加密储存呢?设想如果数据库中直接存入用户的密码,数据库一旦泄露,那用户的信息将暴露无遗。所以将密码加密后存入数据库,将用户密码加密后的信息与存入数据库的信息去比对验证,这样就算数据库泄露,加密后的密码并不能被直接用于登录。

1.MD5加密

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class Main {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		md5.update("XXX123456".getBytes());
		byte[] digestByte = md5.digest();
		System.out.println(Arrays.toString(digestByte));
	}
}

效果如图:
在这里插入图片描述每次加密的长度,相同内容都加密的字节数组都是相同的。
由于加密成字节数组并不方便存储,我们往往会将他转换成十六进制的字符串来代替。为了方便之后的SHA-1,SHA-256等加密的实现,我们自己创建一个HashTool工具类。

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

public class HashTool {
	private static MessageDigest digest;
	private HashTool() {
		super();
	}
public static String digestMD5(String md5) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("MD5");
		digest.update(md5.getBytes());
		return byteToHex(digest.digest());
	}
public static String byteToHex(byte[] b) {
	StringBuilder sb = new StringBuilder();
	for (byte s : b) {
		String format = String.format("%02x", s);
		sb.append(format);
	}
	return sb.toString();
	}
}

本类写了一个静态的byteToHex()方法将加密的字节数组,每一位字节对应两位十六进制数(不足两位用0补充,再将MD5加密的过程封装在digestMD5()方法中,最终调用byteToHex()方法将字节数组转换成十六进制的字符串。
效果如图:
在这里插入图片描述

2.SHA-1,SHA-256,SHA-512加密

同理,这些加密只需要在MessageDigest.getInstance(加密类型)方法中填入想要加密的类型即可,我们在HashTool类中实现,代码如下

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HashTool {
	private static MessageDigest digest;
	private HashTool() {
		super();
	}

public static String digestMD5(String md5) throws NoSuchAlgorithmException {
	digest = MessageDigest.getInstance("MD5");
	digest.update(md5.getBytes());
	return byteToHex(digest.digest());
	}

public static String digestSHA256(String sha256) throws NoSuchAlgorithmException {
	digest = MessageDigest.getInstance("SHA-256");
	return handler(sha256);

	}

public static String digestSHA512(String sha512) throws NoSuchAlgorithmException {
	digest = MessageDigest.getInstance("SHA-512");
	return handler(sha512);

	}

public static String digestSHA1(String sha1) throws NoSuchAlgorithmException {
	digest = MessageDigest.getInstance("SHA-1");
	return handler(sha1);

	}

private static String handler(String handle) {
	digest.update(handle.getBytes());
	return byteToHex(digest.digest());
	}

public static String byteToHex(byte[] b) {
	StringBuilder sb = new StringBuilder();
	for (byte s : b) {
		String format = String.format("%02x", s);
		sb.append(format);
		}
	return sb.toString();
	}
}

效果如图:
在这里插入图片描述(ASH-512字符串太长就不展示完全了)

3.ripeMd160加密

ripeMd160加密是第三方提供的加密包,想要使用ripeMd160加密必须导入bcprov-jdk15on-1.70.jar包。代码如下:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class BouncyDemo {
	public static void main(String[] args) throws NoSuchAlgorithmException {
		// 注册BouncyCastleProvider通知类
		Security.addProvider(new BouncyCastleProvider());
		MessageDigest ripeMd160 = MessageDigest.getInstance("RipeMD160");
		ripeMd160.update("wbjxxmy".getBytes());
		byte[] result = ripeMd160.digest();
		System.out.println("RipeMD160加密后为:" + HashTool.byteToHex(result));
		System.out.println("RipeMD160加密的字节长度为:" + ripeMd160.digest().length);
	}
}

导入jar包后,想要让java程序知道你想使用RipeMD160加密,得先使用Seurity安全工具类的addProvider()方法,new一个BouncyCastleProvider来注册BouncyCastleProvider通知类,通知java程序将可能使用bcprov-jdk15on-1.70.jar包下的算法。
RipeMD160算法加密结果如图:
在这里插入图片描述

4.加盐

以上的加密真的就安全了吗?答案是否定的,我们还要防止彩虹表攻击
彩虹表是一个用于加密散列函数逆运算的预先计算好的表, 为破解密码的散列值(或称哈希值、微缩图、摘要、指纹、哈希密文)而准备。
所以,为了预防彩虹表攻击,我们可以加盐(salt):对每个口令额外添加一个随机数,进行组合。
digest=md5(salt+inputpassword)
经过加盐处理的数据库表如下所示:

username salt password
张三 H1r0a 894152fff4f97e8fb2b8f304c5c56ebb
李四 7$p2w fc195df5b3ff11af8d07e10be912fb44
王五 z5Sk9 1c8854242ab5c4a493a808538be5baf1

还有一种类似加盐,但比加盐更安全的加密方式,详情跳转至Hmac算法。


总结

本文章简单介绍了哈希算法,并且实现了基本的常用的哈希算法对密码的加密。

你可能感兴趣的:(哈希算法,算法,java)