一、概述
Hash加密算法是一种将任意长度的消息压缩成固定长度散列值的算法。它的特点是快速、不可逆和安全。Hash加密算法被广泛用于数字签名、数据完整性验证等信息安全领域。本文将介绍Hash加密算法的基本原理、常用算法和应用场景。
1.1基本原理
Hash加密算法通过将任意长度的消息输入到算法中,经过一系列计算得到一个固定长度的Hash值。Hash值可以看作是消息的指纹,具有唯一性和不可逆性。对于相同的消息,执行相同的Hash算法得到的Hash值是相同的,但即使是输入消息的微小变化也会导致Hash值的巨大变化。因此,Hash加密算法可以用于验证数据完整性和数字签名等场景。
二、哈希碰撞
哈希碰撞是指:两个不同的输入得到了相同的输出。
例如:
"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0
"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03
碰撞能不能避免?答案是不能。碰撞是一定会出现的,因为输出的字节长度是固定的, String 的 hashCode() 输出是 4 字节整数,最多只有 4294967296 种输出, 但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入 集合映射到一个有限的输出集合,必然会产生碰撞。 碰撞不可怕,我们担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系 到哈希算法的安全性。一个安全的哈希算法必须满足: 碰撞概率低; 不能猜测输出。 不能猜测输出是指:输入的任意一个 bit 的变化会造成输出完全不同,这样就很难从输出反推输入(只能依靠暴力穷举)。
三、常用算法
先创建一个工具类,用于将加密结果转换成16进制字符串:
package EnConde.MD5;
public class HashTools {
private HashTools(){}
public static String bytesToHex(byte[] bytes){
StringBuilder ret = new StringBuilder();
for (byte b : bytes) {
ret.append(String.format("%02x", b));
}
return ret.toString();
}
}
2.1MD5算法
MD5算法是一种广泛使用的Hash加密算法。它的输出为128位二进制数,通常用32位十六进制数表示。MD5算法是基于MD4算法的改进版,具有更强的安全性和较快的运算速度。但是,由于其安全性不够高,不建议在安全性要求较高的场景中使用。
package EnConde.MD5;
import java.security.MessageDigest;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws Exception{
//创建基于MD5算法的消息摘要对象
MessageDigest md5 =MessageDigest.getInstance("MD5");
//更新原始对象
md5.update("dadad".getBytes());
//获取加密后的结果
byte[] digestBytes = md5.digest();
System.out.println("加密结果:" + Arrays.toString(digestBytes));
System.out.println("结果(字符串):" + HashTools.bytesToHex(digestBytes));
System.out.println("长度:" + digestBytes.length);
}
}
加密结果:[-31, 10, -36, 57, 73, -70, 89, -85, -66, 86, -32, 87, -14, 15, -120, 62]
结果(字符串):e10adc3949ba59abbe56e057f20f883e
长度:16
2.1.1加盐
哈希算法的一个重要途径就是存储用户密码等重要信息,用户登陆时系统会将用户输入的密码进行加密计算,然后与数据库中存储的加密信息比对,如果相同则密码正确,反之密码错误。这样做即使储存账号密码的数据库泄露,他人也无法知道用户的原始密码。但是要预防彩虹表攻击。
彩虹表攻击(Rainbow Table Attack)是一种密码破解技术,它通过预先生成大量的可能性密码,存储在彩虹表中,然后与目标系统的加密密码匹配,以找出密码的明文。
彩虹表攻击是一种离线攻击方式,攻击者不需要直接与目标系统进行交互,而是使用预先生成的彩虹表进行计算,以枚举可能的密码。因此,彩虹表攻击不需要破解系统的加密算法,而是利用了密码长度和复杂度不足的弱点。
为了防止彩虹表攻击,可以采取一些措施,如使用强密码,使用盐值(salt)等。盐值是一个随机的字符串,与密码结合使用,使得相同的密码在不同的系统上产生不同的加密结果,从而增加了彩虹表攻击的难度。
package EnConde.MD5;
import java.security.MessageDigest;
import java.util.UUID;
public class Test3 {
public static void main(String[] args) throws Exception{
String password = "123456";
//盐值
String random = UUID.randomUUID().toString().substring(0,4);
//创建基于MD5算法的消息摘要对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
//加盐
md5.update(password.getBytes());
md5.update(random.getBytes());
byte[] ret = md5.digest();
System.out.println("加密结果:" + HashTools.bytesToHex(ret));
}
}
加密结果:b70c612966fa90483b9be96ddc90f118
可以看到与加盐前的加密结果不同,更加提高了安全性。
但每次加密的结果都与上一次不同,这是因为每次加的盐值不同,所以加盐后要把盐值也存储到数据库中。
2.2SHA算法
SHA算法是一系列Hash算法的总称,包括SHA-1、SHA-2和SHA-3等。其中,SHA-1算法输出160位的Hash值,SHA-2算法输出256、384或512位的Hash值,SHA-3算法则输出224、256、384或512位的Hash值。SHA-2算法是目前最广泛使用的Hash加密算法之一,具有较高的安全性和可靠性。
package EnConde.SHA1;
import EnConde.MD5.HashTools;
import java.security.MessageDigest;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws Exception{
String password = "123456";
//创建基于SHA1算法的消息摘要对象
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
//更新原始对象
sha1.update(password.getBytes());
//获取加密后的结果
byte[] ret = sha1.digest();
System.out.println("加密结果:" + Arrays.toString(ret));
System.out.println("结果(字符串):" + HashTools.bytesToHex(ret));
System.out.println("长度:" + ret.length);
}
}
加密结果:[124, 74, -115, 9, -54, 55, 98, -81, 97, -27, -107, 32, -108, 61, -62, 100, -108, -8, -108, 27]
结果(字符串):7c4a8d09ca3762af61e59520943dc26494f8941b
长度:20
具体算法与MD5算法大同小异,只是将getInstance()中的MD5改成SHA-1,SHA-256,SHA-512等也是同理,因为这些都是java标准库支持的哈希算法。
可以看到与MD5算法最大的区别就是固定长度不同。
2.3RipeMD160算法
RipeMD160算法是一种安全哈希函数,用于将任意长度的数据(字符串、文件等)压缩成160位的哈希值。它是对RIPEMD算法的改进和升级,于1996年面向欧洲信息技术标准委员会(CEN)提出,并获得了国际标准化组织(ISO)的认可。
RipeMD160算法与MD5、SHA-1等哈希算法相比,具有更大的哈希值输出长度、更高的安全性和更好的性能。同时,它也可以应用于数字签名、数据完整性验证和消息认证等领域。
但java标准库不支持该算法,所以需要导入jar(bcprov-jdk15on-1.70.jar)。
package EnConde.RipeMD160;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws Exception{
//注册BouncyCastleBouncyCastleProvider通知类
//将提供的消息摘要算法注册至Security
Security.addProvider(new BouncyCastleProvider());
//获取RipeMD160算法的加密对象
MessageDigest ripeMd160 = MessageDigest.getInstance("RipeMD160");
//更新原始数据
ripeMd160.update("123456".getBytes());
//获取消息摘要(加密)
byte[] ret = ripeMd160.digest();
//消息摘要的字节长度和内容
System.out.println("结果(长度):" + ret.length);
System.out.println("内容:" + Arrays.toString(ret));
//16进制内容字符串
String hex = new BigInteger(1, ret).toString(16);
System.out.println("结果(长度):" + hex.length());
System.out.println("内容:" + hex);
}
}
结果(长度):20
内容:[-68, -7, 122, -57, 43, -122, 116, 20, -96, 11, 84, -80, -36, 64, -32, -128, 82, -76, 104, 79]
结果(长度):40
内容:bcf97ac72b867414a00b54b0dc40e08052b4684f
三、应用场景
3.1数据完整性验证
Hash加密算法可以用于验证数据的完整性。发送方对数据计算Hash值,并将其发送给接收方。接收方对接收到的数据计算Hash值,如果两个Hash值相同,则可以确认数据没有被篡改。这种应用场景在互联网数据传输中非常常见,例如网站提供下载的软件文件通常会提供md5或sha1校验值,让用户利用Hash算法验证文件的完整性。
3.2数字签名
Hash加密算法可以用于数字签名,即对数字信息进行签名,让接收方确认发送方的身份和内容的确是由发送方所发出的。发送方对消息计算Hash值,并使用自己的私钥对Hash值进行加密,形成数字签名并发送给接收方。接收方对消息计算Hash值,并使用发送方的公钥对数字签名进行解密,得出Hash值并与接收到的Hash值进行比对。如果两个Hash值相同,则确认发送方的身份和消息的完整性,数字签名有效。
四、总结
Hash加密算法是一种快速、不可逆和安全的算法,被广泛应用于数字签名、数据完整性验证等信息安全领域。常见的Hash算法包括MD5和SHA算法。Hash算法的应用场景包括数据完整性验证和数字签名等。