常用的哈希算法总结

文章正文
发布时间:2024-12-11 09:11

        哈希算法又称位摘要算法,它的作用是”对任意一致输入数据进行计算,得到一个固定长度的输出摘要。

        哈希算法的特点:

        1、相同的输入一定得到相同的输出;

        2、不同的输入大概率得到不同的输出。

哈希算法的目的是为了验证原始数据是否被篡改

2、哈希碰撞

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

        哈希碰撞无法避免,是一定会出现的,因为输出的字节长度是固定的。但是输入的数据长度是不固定的,有无数种输入,所以哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会发生碰撞。

所以,一个安全的哈希算法必须满足:

1、碰撞概率低

2、不能猜测输出:输入的任意一个bit的变化会造成输出完全不同,这样就很难从输出反推输入。

3、常用的哈希算法

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

算法输出长度(位)输出长度(字节)
MD5   128 bits   16  
SHA-1   160 bits   20  
RipeMD-160   160 bits   20  
SHA-256   256 bits   32  
SHA-512   512 bits   64  
1、MD5加密算法 import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; //MD5加密 public class Test06 { public static void main(String[] args) { try { // 创建基于MD5的消息摘要对象 MessageDigest md5 = MessageDigest.getInstance("MD5"); // 更新原始数据 输入数据 md5.update("叮叮车".getBytes()); // 获取加密后的结果 byte[] res = md5.digest(); System.out.println("加密后的结果:" + Arrays.toString(res)); System.out.println("十六进制字符串:" + HashTools.BytesToHex(res)); System.out.println("加密结果的长度:" + res.length); System.out.println(); // 更新原始数据 输入数据 md5.update("叮叮".getBytes()); md5.update("车".getBytes()); // 获取加密后的结果 byte[] ress = md5.digest(); System.out.println("加密后的结果:" + Arrays.toString(ress)); System.out.println("十六进制字符串:" + HashTools.BytesToHex(ress)); System.out.println("加密结果的长度:" + ress.length); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } }

        首先我们需要创建一个基于MD5加密算法的摘要对象MessageDigest,然后使用update方法更新原始数据,在使用md5.digest()获取加密后的结果。从上面的代码我们可以看到,该结果是一个byte类型的数组,并且该数组的长度固定位16,不会改变。然后我们提供一个HashTools的工具类,将获取的结构转换为十六进制的格式进行输出。

import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; 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); } public static String handler(String source) { digest.update(source.getBytes()); byte[] buf = digest.digest(); String hex = BytesToHex(buf); return hex; } // 将自己数组转换为十六进制字符串 public static String BytesToHex(byte[] buff) { StringBuilder res = new StringBuilder(); for (byte b : buff) { res.append(String.format("%02x", b)); } return res.toString(); } }

我们还可以使用MD5算法对图片进行加密

import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; // 图片加密 public class Test07 { public static void main(String[] args) throws IOException, NoSuchAlgorithmException { byte[] imageBytes = Files.readAllBytes(Paths.get("D:\\java\\ddc.jpg")); MessageDigest md5 = MessageDigest.getInstance("md5"); md5.update(imageBytes); // 获取加密摘要 byte[] res = md5.digest(); System.out.println(Arrays.toString(res)); System.out.println(HashTools.BytesToHex(res)); System.out.println(res.length);// MD5算法输出的长度固定为16个字节 } }

        对图片加密与对字符串加密的大致步骤是一样的,对图片的加密需要我们以流的方式获取图片,最后我们发现加密后的结果还是16位。

        对数据进行加密的作用主要是为了防止数据泄露,为了避免彩虹表的攻击,我们可以采取特殊措施来抵御彩虹表的攻击,这个方法位加盐(salt)

import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.UUID; public class Test08 { public static void main(String[] args) throws NoSuchAlgorithmException { String password = "ddcwxhn"; // 加上盐值 随机 String salt = UUID.randomUUID().toString().substring(0, 4); // 产生md5算法 MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(password.getBytes()); md5.update(salt.getBytes()); byte[] ret = md5.digest(); String res = HashTools.BytesToHex(ret); System.out.println(res); } }

         加盐其实就是在更新原始数据的时候需要加上一个你自己设置的值 同时让这个值参与加密,使我们最后的结果是无法对照的,需要注意的是,我们必须要记录这个盐值,否则将无法找回。

2、SHA加密算法

SHA加密算法的使用和MD5是一样的,只需要将MD5改为SHA-1就可以。

import java.security.NoSuchAlgorithmException; public class Test09 { public static void main(String[] args) throws NoSuchAlgorithmException { String password = "ddcloveyou"; String md5 = HashTools.digestByMD5(password); String SHA1 = HashTools.digestBySHA1(password); String SHA256 = HashTools.digestBySHA256(password); String SHA512 = HashTools.digestBySHA512(password); System.out.println(md5); System.out.println(SHA1); System.out.println(SHA256); System.out.println(SHA512); } }

 3、RipeMD-160加密算法

        这是一个由第三方开源库提供的RipeMD160消息摘要算法实现,需要注册BouncyCastleProvider通知类,将提供的消息摘要算法注册至Security,然后就可以使用。

import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.util.Arrays; import org.bouncycastle.jce.provider.BouncyCastleProvider; // 使用第三方开源库提供的RipeMD160消息摘要算法实现 public class Demo03 { public static void main(String[] args) throws NoSuchAlgorithmException { // 注册BouncyCastleProvider通知类 // 将提供的消息摘要算法注册至Security Security.addProvider(new BouncyCastleProvider()); MessageDigest md = MessageDigest.getInstance("RipeMD160"); md.update("ddcloveyou".getBytes()); byte[] res = md.digest(); System.out.println(Arrays.toString(res)); System.out.println(HashTools.BytesToHex(res)); System.out.println(res.length); } }

使用该算法需要导入jar包:bcprov-jdk15on-1.70.jar 

4、Hmac算法

        Hmac算法是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法,Hmac算法总是和某种哈希算法配合起来使用,例如HmacMD5算法,所以HmacMD5算法可以看作带有一个安全的key的MD5算法。

1、HmacDM5使用的可以长度位64字节,更安全;

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

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

import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; //Hmac算法 public class Demo01 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { String password = "ddcloveyou"; // 获取HmacDM5密钥生成器 KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); // 生成密钥 SecretKey key = keyGen.generateKey(); System.out.println(Arrays.toString(key.getEncoded())); System.out.println(key.getEncoded().length); System.out.println(HashTools.BytesToHex(key.getEncoded())); // 使用密钥 进行加密 // 获取Mac加密算法对象 Mac mac = Mac.getInstance("HmacMD5"); mac.init(key);// 初始化密钥 mac.update(password.getBytes());// 更新原始密钥 byte[] res = mac.doFinal();// 加密处理 获取加密结果 System.out.println("加密后的:" + HashTools.BytesToHex(res)); System.out.println(res.length); } }

        使用Hmac算法我们需要使用KeyGenerator获取密钥生成器,然后使用SecretKey生成密钥,然后使用密钥进行加密,获取Mac加密算法对象,然后需要先使用init方法初始化密钥,然后同样使用update方法来更新原始密钥,最后使用doFinal方法进行加密处理,获取加密结果,结构同样是一个字节数组。

当我们需要验证的时候,由两种方式恢复密钥

1、 按照字节数组恢复 import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; // 按照字节数组恢复密钥 public class Demo02 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { String password = "ddcloveyou"; byte[] buf = { -53, 109, 14, 88, -85, 42, 116, 121, -78, 115, -103, -84, 72, 61, -51, 102, 40, -5, -38, -103, -116, 67, -5, 39, 92, -54, -100, -12, -20, 31, -1, 18, 34, 71, -96, -38, 25, 49, -48, -76, 98, -126, -106, -37, 31, 70, 20, -117, -55, -48, 67, 65, 97, -46, -15, -92, -78, 8, -108, -43, -94, 85, -120, 125 }; // 恢复密钥 SecretKey key = new SecretKeySpec(buf, "HmacMD5"); Mac mac = Mac.getInstance("HmacMD5"); mac.init(key); mac.update(password.getBytes()); String res = HashTools.BytesToHex(mac.doFinal()); System.out.println(res); // 67bf43d45007ab92eeae124c981bff53 } } 2、按照密钥(字符串)恢复 import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; // 按照字节数组恢复密钥 public class Demo02 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { // 按照密钥 String str = "81dd6754fc06525ed648bf188a355e5b5691c3839558ee33a9de4e01c0c19de9e5d8b4f6b606c882d87ea3be93cb7755d6d95697666f0b86372cca1d4058ce5b"; // 用于保存密钥 byte[] keyByte = new byte[64]; for (int i = 0, k = 0; i < str.length(); i += 2, k++) { String s = str.substring(i, i + 2); keyByte[k] = (byte) Integer.parseInt(s, 16); } // 恢复密钥 SecretKey key = new SecretKeySpec(keyByte, "HmacMD5"); Mac mac = Mac.getInstance("HmacMD5"); mac.init(key); mac.update(password.getBytes()); String res = HashTools.BytesToHex(mac.doFinal()); System.out.println(res); // 67bf43d45007ab92eeae124c981bff53 } }

以上就是比较基础的哈希算法 

首页
评论
分享
Top