一、概述
哈希算法(Hash)又称摘要算法(Digest)。它可以对任意一组输入数据进行计算,得到一个固定长度的输出摘要。哈希算法的目的:验证原始数据是否被修改。
二、哈希碰撞
哈希碰撞就是输入两个不同的数据,得到两个相同的结果。哈希碰撞不能避免,所以我们可以利用一些算法来减少哈希碰撞发生的概率。
三、彩虹表攻击
直接将用户的原始口令存放在数据库中,数据库一旦泄露,黑客即可获取用户信息。当黑客获取到MD5口令后,通过暴力穷举的方法就可以反推用户密码。但这样费时费力,所以就有了一个预先计算好的常用口令和它们的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);
}
//通过消息摘要对象,处理加密内容
private 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 ret = new StringBuilder();
for(byte b : bytes) {
//将字节值转换为两位十六进制字符串
ret.append(String.format("%02x", b));
}
return ret.toString();
}
}
1、MD5
MD5算法的固定输出长度为16个字节
利用MD5算法实现文字加密,示例代码
//创建基于MD5算法的消息摘要对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
//更新原始数据
md5.update("高堂明镜悲白发,朝如青丝暮成雪".getBytes());
//获取加密后的结果
byte[] mdbytes = md5.digest();
System.out.println("加密后的结果(字节数组):"+Arrays.toString(mdbytes));
System.out.println("加密后的结果(字符数组):"+HashTools.bytestoHex(mdbytes));
System.out.println("加密后的长度:"+mdbytes.length);
利用MD5算法实现图片加密,示例代码
//按照MD5算法对图片进行加密
//图片的原始字节内容
byte[] imageBuff = Files.readAllBytes(Paths.get("E:\\test\\love.jpg"));
//创建基于MD5算法的消息摘要对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
//原始字节内容(图片)
md5.update(imageBuff);
//获取加密摘要
byte[] mdbytes = md5.digest();
System.out.println("加密后的结果(字节数组):"+Arrays.toString(mdbytes));
System.out.println("加密后的结果(字符数组 ):"+HashTools.bytestoHex(mdbytes));
System.out.println("加密后的长度:"+mdbytes.length);
2、SHA-1
通过随机加盐解决彩虹表攻击问题,示例代码
//原始密码
String password = "gtbjbbf";
//产生随机的盐值
//盐值的位数和内容可以自己设定,比如我所设定的五位随机数
String salt = UUID.randomUUID().toString().substring(0,5);
//创建基于SHA-1算法的消息摘要对象
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
sha1.update(password.getBytes());//原始密码
sha1.update(salt.getBytes());//盐值
//计算加密结果,SHA-1的输出结果为20个字节(40个字符)
String saltRet = HashTools.bytestoHex(sha1.digest());
System.out.println(saltRet);
3、SHA-256
类似于MD5和SHA-1算法,只需要将传入名称改为SHA-256即可。
4、SHA-512
调用工具类方法进行计算,示例代码
try {
String md5 = HashTools.digestByMD5("gtmjbbf");
String sha1 = HashTools.digestBySHA1("gtmjbbf");
String sha256 = HashTools.digestBySHA256("gtmjbbf");
String sha512 = HashTools.digestBySHA512("gtmjbbf");
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();
}
5、RipeMd-160
使用第三方开源库提供的RipeMD-160消息摘要算法实现步骤:
1、注册BouncyCastleProvider通知类,将提供的消息摘要算法注册至Security。
2、获取RipeMD-160算法的“消息摘要对象”(加密对象)
3、更新原始数据
4、获取消息摘要(加密)
示例代码
//使用第三方开源库提供的RipeMD-160消息摘要算法实现
//注册BouncyCastleProvider通知类
//将提供的消息摘要算法注册至Security
Security.addProvider(new BouncyCastleProvider());
//获取RipeMD-160算法的“消息摘要对象”(加密对象)
MessageDigest ripeMd160 = MessageDigest.getInstance("RipeMD160");
//更新原始数据
ripeMd160.update("gtmjbbf".getBytes());
//获取消息摘要(加密)
byte[] result = ripeMd160.digest();
//消息摘要的字节长度和内容
System.out.println("加密结果(字节长度):"+result.length);
System.out.println("加密结果(字节内容):"+Arrays.toString(result));
//16进制内容字符串
String hex = new BigInteger(1,result).toString(16);
System.out.println("加密结果(字符串长度):"+hex.length());
System.out.println("加密结果(字符串内容):"+hex);
6、Hmac
Hmac算法是一种基于密钥的消息认证码算法,全称Hash-base Message Authentication Code,是一种更安全的算法。它总是和某种哈希算法配合起来用的。
例如HmacMD5:
Hmac算法示例代码
//Hmac算法
//获取HmacMD5密钥生成器
KeyGenerator KeyGen = KeyGenerator.getInstance("HmacMD5");
String password = "gtmjbbf";
//生成密钥
SecretKey key = KeyGen.generateKey();
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(password.getBytes());//更新元原始加密内容
byte[] bytes = mac.doFinal();//加密处理,并获取加密结果
String ret = HashTools.bytestoHex(bytes);//加密结果处理成16进制字符串
System.out.println("加密结果:"+ret);
System.out.println("加密结果长度(字符):"+ret.length());
System.out.println("加密结果长度(字节):"+bytes.length);
按照“字节数组”,恢复Hmac密钥,示例代码
//原始密码
String password = "gtmjbbf";
//密钥(字节数组)
byte[] keyBytes = {-120, -82, 22, 60, 15, -23, -94, -75, 109, -39, 17, -50, 4, 83, -88, -3, -126, 86, -28, 47, -54, -119, -76, -52, 14, 87, -8, 101, 82, -54, 79, 122, -116, 19, 73, 75, 22, 25, 80, -101, 111, -45, -70, 8, -31, 125, -123, 23, 114, -88, 111, -38, 114, -77, 98, 126, 99, 21, 84, -11, 119, 81, -62, 83};
//恢复密钥(字节数组)
SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5");
//创建加密算法对象
Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);//初始化密钥
mac.update(password.getBytes());
byte[] bytes = mac.doFinal();//加密处理,并获取加密结果
String ret = HashTools.bytestoHex(bytes);
System.out.println(ret);//7a9542533a479363a848f2ba26ab1861
按照“字符串”,恢复Hmac密钥,示例代码
String password = "gtmjbbf";
//密钥(字符串)
String keyStr = "9ccc5b3bb0cca1239090646812eb724459fbeeb7bd21bb1d3ecd7e4d74a63ce244bf1abd446cc4fd2592bbe66e16a09e65febbe9c8dd3a682fd52edfb7461535";
//用于保存密钥:密钥长度为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值
}
SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5");
Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);
mac.update(password.getBytes());
byte[] bytes = mac.doFinal();//加密处理,并获取加密结果
String ret = HashTools.bytestoHex(bytes);
System.out.println("加密结果:"+ret);//d46d6b66f146bd468d19800671dc7594
System.out.println("加密结果长度(字符):"+ret.length());
System.out.println("加密结果长度(字节):"+bytes.length);