常用的哈希算法总结

1、概述

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

        哈希算法的特点:

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

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

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

2、哈希碰撞

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

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

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

1、碰撞概率低

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

3、常用的哈希算法

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

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

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

	}
}

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
哈希表是一种常用的数据结构,它通过哈希函数将键映射到存储位置,以实现高效的数据查找和插入操作。哈希函数是一种提取数据特征的算法,根据不同的数据形式和场景,可以选择不同的哈希算法。常见的哈希算法包括MD5等。\[1\] 在哈希表中,哈希函数的优劣直接影响到哈希表的查找效率。优秀的哈希函数可以减少冲突的发生,提高查找效率。哈希函数的设计方法有多种,其中常见的包括直接寻址法、除留余数法、平方取中法等。不同的哈希函数适用于不同的数据类型和规律。\[3\] 哈希冲突是指不同的键经过哈希函数计算后得到相同的哈希值,导致数据存储位置冲突的情况。为了解决哈希冲突,常用的方法有开放寻址法和链地址法。开放寻址法是指当发生冲突时,通过一定的规则在哈希表中寻找下一个可用的位置来存储数据。链地址法是指在哈希表的每个位置上维护一个链表,将哈希值相同的键值对存储在同一个链表中。\[2\] 总结来说,哈希表是一种通过哈希函数将键映射到存储位置的数据结构,常用哈希算法有多种,哈希函数的设计方法也有多种,而哈希冲突的处理方法包括开放寻址法和链地址法。这些算法和数据结构的选择取决于具体的应用场景和需求。 #### 引用[.reference_title] - *1* [详解哈希数据结构,手写哈希表](https://blog.csdn.net/CRMEB/article/details/120820682)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [数据结构之哈希表以及常用哈希的算法表达(含全部代码)](https://blog.csdn.net/weixin_53050357/article/details/126666617)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [哈希表-数据结构(C语言)](https://blog.csdn.net/weixin_44681349/article/details/124782035)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农叮叮车

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值