Java的Hash算法及相应的Hmac算法

news2024/9/20 3:22:59

【相关知识】 

加密算法知识相关博文:浅述.Net中的Hash算法(顺带对称、非对称算法)-CSDN博客  

【出处与参考】 

  • MessageDigest 类介绍、分多次调用update方法与一次性调用一致的说明引自:

https://blog.csdn.net/cherry_chenrui/article/details/99412886

  • Java Hash算法工具类引自:

https://blog.csdn.net/c1390527393/article/details/131643488

  • 非英文及利用图片进行Hash加密引自:

https://blog.csdn.net/m0_64978052/article/details/131643676

概述

哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
哈希算法最重要的特点就是:
●相同的输入一定得到相同的输出;
●不同的输入大概率得到不同的输出。
所以,哈希算法的目的:为了验证原始数据是否被篡改。

常用哈希算法 

常用哈希算法
算法输出长度(位)输出长度(字节)

MD5

128 bits

16 bytes

SHA-1

160 bits

20 bytes

RipeMD-160

160 bits

20 bytes

SHA-256

256 bits

32 bytes

SHA-512

512 bits

64 bytes

MessageDigest

java.security.MessageDigest是java提供的加密API
作用:提供MD5,SHA-1,SHA-256,SHA-512等Hash加密算法。可接受任意长度的输入,并产生固定长度的输出(输出一般可称为摘要或散列)。

通常步骤如下:

//1. 实例化一个MessageDigest对象,通过提供的静态的getInstance方法。
MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); //参数值"SHA1"指的是加密的算法,大小写无所谓
//2. 输入待加密的字符串
messageDigest.update("待加密的字符串");
//3. 加密之后生成的密文的字节数组
byte[] value = messageDigest.digest(); 
//(一般不会直接使用生成的字节数组,而是转化成16进行字符串,长度一般可以设定)

【Tips】:MessageDigest调用digest()方法之后,MessageDigest将被重置,可以进行开始新的加密。

下面提供字节数组转化为16进制字符串的方法

/**
 * 字符串数组解析成16进制字符串
 *  md : 待转化的字节数组
 * needLen: 需要转化的16进制字符串的长度,一般都是偶数
 * 说明:此算法可以设定生成的16进制字符串的长度,是拿原字节数组的前needLen/2长度的字节数组转化而来的
 *       如果不需要特定长度,直接全部转,可以设置needLen的长度为md.length*2,获取去掉needLen,设定buf的长度为j*2,for循环的      
 *       终止条件为i<j*2 即可
 * */
private static String tranform16Str(byte[] md, int needLen){
    char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9',
            'a','b','c','d','e','f'};
    try {
        int j = md.length;
        char buf[] = new char[needLen];
        int k = 0;
        for (int i = 0; i < needLen/2; i++) {
            byte byte0 = md[i];
            buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
            buf[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(buf);
    } catch (Exception e) {
        log.error("加密后的密文转化为16进制字符串过程中出现异常,",e);
    }
    return null;
}

假如利用一个文件作为Hash加密的输入:

1. 首先先把文件读取到一个字节数组里面
File file = new File(filePath);
InputStream in = new FileInputStream(file);
byte[] allData = readInputStream(in);//获取到文件的内容

2. 接下来可以有两种方式:

方式1:一段一段往里面塞

int len = allData.length;
int i = 0;
while(true){
    try{
        int arrLen = (len - i * 4096) > 4096 ? 4096 : (len - i * 4096);
        byte[] content = new byte[arrLen];
        System.arraycopy(getData, i * 4096, content, 0, arrLen);
        messageDigest.update(content);
        i++;
    }catch (Exception e){
        log.info("字节数组拷贝出现异常,表示完成 i ={}", i);
        break;
    }
}

byte[] transform = messageDigest.digest();
//说明,MessageDigest调用digest()方法之后  输入的摘要将被重置,意思就是之后需要再加密的话  可以直接使用之前已有的对象
String miwen = tranform16Str(transform, transform.length);

方式2:一次性全部往里面塞

messageDigest.update(allData);

byte[] second = messageDigest.digest();

之后再进行16进制的转换操作。

上述两种方式的结果拿到的是一样的。
说明多次的update操作(digest方法之前)只是单纯的输入内容的追加操作

/**
* 获取输入流中的内容到字节数组里面
**/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
    byte[] buffer = new byte[1024];
    int len = 0;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    while ((len = inputStream.read(buffer)) != -1) {
        bos.write(buffer, 0, len);
    }
    bos.close();
    return bos.toByteArray();
}

hash算法(消息摘要算法)工具类

此工具类可以有效帮助实现其他hash算法相同的功能

public class HashTools {
    private static MessageDigest digest;
 
    private HashTools(){}
 
    //将字节数组转换为16进制字符串
    public static String bytesToHex(byte[] bytes){
        StringBuilder ret =new StringBuilder();
        for (byte b :bytes) {
            //将字节数组转换为2位16进制字符串
            ret.append(String.format("%02x",b));
        }
        return ret.toString();
    }
 
    //按照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;
    }

如果只是想要通过MD5,SHA-1,SHA-256,SHA-512进行加密

直接调用此工具类中静态方法即可

        //MD5算法加密后生成的字符串
        System.out.println("MD5="+HashTools.digestByMD5("wbjxxmy"));
        //SHA-1算法加密后生成的字符串
        System.out.println("SHA-1="+HashTools.digestBySHA1("wbjxxmy"));
        //SHA-256算法加密后生成的字符串
        System.out.println("SHA-256="+HashTools.digestBySHA256("wbjxxmy"));
        //SHA-512算法加密后生成的字符串
        System.out.println("SHA-512="+HashTools.digestBySHA512("wbjxxmy"));

输出结果

但此加密依然存在风险,需注意彩虹表攻击

为了采取特殊措施来抵御彩虹表攻击:我们可以对每个口令额外添加随机数,这个方法称之为加盐(salt)

以MD5算法为例:

加盐的MD5算法 


        //原始密码
        String passWord ="wbjxxmy";
 
        //产生随机的盐值(以随机生成的UUID前四位为例)
        String sale = UUID.randomUUID().toString().substring(0,4);
 
        //创建基于MD5算法的消息摘要对象
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(passWord.getBytes());//原始密码
        digest.update(sale.getBytes());//加盐
 
        //生成的加密结果MD5输出结果位20个字节(40个字符)
        System.out.println(Arrays.toString(digest.digest()));//20个字节
        System.out.println(HashTools.bytesToHex(digest.digest()));//40长度的字符串

RipeMD160算法

Java标准库并没有提供RipeMD160算法

我们需要找一个现成的第三方库,直接使用。

BouncyCastle就是一个提供了很多哈希算法和加密算法的第三方开源库。它提供了Java标准库没有的一些算法,例如,RipeMD160哈希算法。

首先,我们必须把BouncyCastle提供的bcprov-jdk15on-1.70.jar添加至classpath。这个jar包可以从官方网站下载

其次,Java标准库的java.security包提供了一种标准机制,允许第三方提供商无缝接入。我们要使用BouncyCastle提供的RipeMD160算法,需要先把BouncyCastle注册一下:

        // 注册BouncyCastle提供的通知类对象BouncyCastleProvider
        Security.addProvider(new BouncyCastleProvider());
 
        // 获取RipeMD160算法的"消息摘要对象"(加密对象)
        MessageDigest md = MessageDigest.getInstance("RipeMD160");
 
        // 更新原始数据
        md.update("wbjxxmy".getBytes());
 
        // 获取消息摘要(加密)
        byte[] result = md.digest();
 
        // 消息摘要的字节长度和内容
        System.out.println(result.length); // 160位=20字节
        System.out.println(Arrays.toString(result));
 
        // 16进制内容字符串
        String hex = new BigInteger(1,result).toString(16);
        System.out.println(hex.length()); // 20字节=40个字符
        System.out.println(hex);

HMac算法

Hmac算法就是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
Hmac算法总是和某种哈希算法配合起来用的。例如,我们使用MD5算法,对应的就是Hmac MD5算法,它相当于“加盐”的MD5:HmacMD5 ≈ md5(secure_random_key, input)
因此,HmacMD5可以看作带有一个安全的key的MD5。使用HmacMD5而不是用MD5加salt,有如下好处:
●HmacMD5使用的key长度是64字节,更安全;
●Hmac是标准算法,同样适用于SHA-1等其他哈希算法;
●Hmac输出和原有的哈希算法长度一致。

可见,Hmac本质上就是把key混入摘要的算法。验证此哈希时,除了原始的输入数据,还要提供key。为了保证安全,我们不会自己指定key,而是通过Java标准库的KeyGenerator生成一个安全的随机的key。

HMac 加密
        // 获取HmacMD5秘钥生成器
        KeyGenerator keyGenerator =KeyGenerator.getInstance("HmacMD5");
        // 产生秘钥
        SecretKey key = keyGenerator.generateKey();
        // 打印随机生成的秘钥:
        System.out.println("字节密钥:"+Arrays.toString(key.getEncoded()));//字节输出
        System.out.println("字符密钥:"+HashTools.bytesToHex(key.getEncoded()));//字符输出
 
        // 使用HmacMD5加密
        Mac mac =Mac.getInstance("HmacMD5");
        // 初始化秘钥
        mac.init(key);
        //对Mac实例反复调用update(byte[])输入数据
        mac.update("wbjxxmy".getBytes());
        //调用Mac实例的doFinal()获取最终的哈希值。
        byte[] bytes = mac.doFinal();
        System.out.println("加密后字节:"+Arrays.toString(bytes));
        System.out.println("加密后字符:"+HashTools.bytesToHex(bytes));

不过每次生成的密钥都是不同的

切记将其保存

HMac密码的校验

如果我们想要验证该密码,需通过密钥的字节数组或字符串和原始密码通过加密对比

按照“key的字节数组+原始输入”计算HMac密钥用于检验
        // 原始密码
        String password = "nhmyzgq";
 
        // 通过"秘钥的字节数组",恢复秘钥
        byte[] bytes ={97, -43, 1, -26, 19, 117, 107, 67, -43, -77, -70, 55, -49, 11, 115,-112, -22, 121, -28, -13, 42, -34, 21, -71, -80, 127, 33, -37, 11, 98, 45, -96, -104, -77, 46, -11, 14, 119, -115, -17, 83, -121, -98, 111, 17, -73, -18, -31, -12, 65, 5, 20, 117, 49, -79, -83, 94, 115, 67, -13, 113, 35, 102, -120};
 
        //恢复密钥
        SecretKey key = new SecretKeySpec(bytes,"HmacMD5");
        // 加密
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(key);
        mac.update(password.getBytes());
        System.out.println("加密结果:" +HashTools.bytesToHex(mac.doFinal()));
按照“key的字符串+原始输入”计算HMac密钥用于检验
        // 原始密码
        String password = "nhmyzgq";
 
        //使用字符串密钥 校验
        String keyWord ="61d501e613756b43d5b3ba37cf0b7390ea79e4f32ade15b9b07f21db0b622da098b32ef50e778def53879e6f11b7eee1f44105147531b1ad5e7343f371236688";
 
        byte[] bytes =new byte[64];
        //将字符密钥以每两个字符转换位一个字节
        for (int i = 0,k=0; i <keyWord.length() ; i+=2,k++) {
            String s = keyWord.substring(i, i + 2);
            bytes[k] = (byte)Integer.parseInt(s,16);
        }
 
        //恢复密钥
        SecretKey key = new SecretKeySpec(bytes,"HmacMD5");
        // 加密
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(key);
        mac.update(password.getBytes());
        System.out.println("加密结果:" +HashTools.bytesToHex(mac.doFinal()));

非英文信息Hash加密

通过对以上Hash算法实现的了解,可以知道Hash加密并不一定要是英文信息,可以是中文甚至是图片来进行Hash加密

1. 中文加密 
//创建基于MD5的消息摘要对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
//更新原始数据
md5.update("何事西风悲画扇".getBytes());
//获得加密数据
byte[] digestBytes = md5.digest();
System.out.println("加密后的结果:"+Arrays.toString(digestBytes));
System.out.println("加密后的结果(16进制):"+Hashtools.BytestoHex(digestBytes));
System.out.println("加密结果长度:"+digestBytes.length);
		
//1、何事西
//2、风悲画扇
//分两次更新数据只要顺序相同获得的加密结果也是一样的
2. 图片加密 
//获取图片信息
byte[] bs = Files.readAllBytes(Paths.get("D:\\3yue\\vv.jpg"));
//创建基于MD5的基本摘要信息
MessageDigest digest = MessageDigest.getInstance("MD5");
//更新数据
digest.update(bs);
//获得加密数组结果并输出
byte[] digestBytes = digest.digest();
System.out.println("加密后的结果:"+Arrays.toString(digestBytes));
//此处为一个Hashtools类写有BytestoHex
//是一个将数据转换为16进制的方法
System.out.println("加密后的结果(16进制):"+Hashtools.BytestoHex(digestBytes));
System.out.println("加密结果长度:"+digestBytes.length);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1612776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024 IDM最新破解版及软件介绍

*IDM&#xff1a;信息时代的高效管理工具** 在快节奏的现代社会中&#xff0c;随着信息的爆炸式增长&#xff0c;如何高效、有序地管理信息成为每个人都需要面对的挑战。IDM&#xff0c;作为一种信息管理工具&#xff0c;正在逐渐受到人们的青睐。 IDM&#xff0c;全称Inform…

HAL STM32 I2C方式读取MT6701磁编码器获取角度例程

HAL STM32 I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f388;《STM32 软件I2C方式读取MT6701磁编码器获取角度例程》&#x1f4cc;MT6701当前最新文档资料&#xff1a;https://www.magntek.com.cn/u…

Scanpy(1)数据结构和样本过滤

注&#xff1a;主要讲述scanpy处理数据的结构、数据过滤&#xff08;生信领域&#xff09;和数据预处理&#xff08;和机器学习类似&#xff0c;但是又有不同。&#xff09; 1. Scanpy简介与安装 Scanpy 是一个可扩展的工具包&#xff0c;用于分析与 AnnData&#xff08;一种…

git 小记

一、 github新建仓库 git clone 。。。。。。。。。。。 &#xff08;增删查补&#xff0c;修改&#xff09; git add . git commit -m "修改” git push (git push main) 二、branch 分支 branch并不难理解&#xff0c;你只要想像将代码拷贝到不同目录…

ruoyi-vue前端的一些自定义插件介绍

文章目录 自定义列表$tab对象打开页签关闭页签刷新页签 $modal对象提供成功、警告和错误等反馈信息&#xff08;无需点击确认&#xff09;提供成功、警告和错误等提示信息&#xff08;类似于alert&#xff0c;需要点确认&#xff09;提供成功、警告和错误等提示信息&#xff08…

restful请求风格的增删改查-----修改and删除

一、修改&#xff08;和添加类似&#xff09; 前端&#xff1a; <script type"text/javascript">function update(){//创建user对象var user {id:$("#id").val(),username:$("#username").val(),password:$("#password").val…

排序 “贰” 之选择排序

目录 ​编辑 1. 选择排序基本思想 2. 直接选择排序 2.1 实现步骤 2.2 代码示例 2.3 直接选择排序的特性总结 3. 堆排序 3.1 实现步骤 3.2 代码示例 3.3 堆排序的特性总结 1. 选择排序基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个…

又来!黄金主题LOF(161116)溢价40%开放申购,拖拉机都开冒烟了!

查看基金公告&#xff0c;黄金主题LOF(161116)下周一(4月22号)开放申购&#xff0c;限额100元&#xff0c;目前溢价40%&#xff0c;可以一拖七套利。 这熟悉的配方&#xff0c;这熟悉的套路&#xff01;一个月前的今天&#xff0c;我好像在标普500LOF上见过。又是易方达这个狗基…

数据结构_时间复杂度

✨✨所属专栏&#xff1a;数据结构✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 什么是时间复杂度&#xff1f; 时间复杂度的定义&#xff1a;在计算机科学中&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定量描述了该算法的运行时间。一个算法执行所耗费的时间&#xff0…

上位机图像处理和嵌入式模块部署(树莓派4b和类muduo网络编程)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 既然是linux编程&#xff0c;那么自然少不了网络编程。在linux平台上面&#xff0c;有很多的网络编程库可以选择&#xff0c;大的有boost、qt&…

python3如何提取汉字

采用正则表达式的方法对字符串进行处理。 str1 "&#xff5b;我%$是&#xff0c;《速$.度\发》中 /国、人"&#xff08;1&#xff09;提取汉字 汉字的范围为”\u4e00-\u9fa5“&#xff0c;这个是用Unicode表示的。 import re res1 .join(re.findall([\u4e00-\u9fa…

如何对图片进行压缩和缩放

在手机像素越来越高的时代&#xff0c;照片的体积也在不断地膨胀&#xff0c;大部分情况下我们是不需要这么大的图片的&#xff0c;这个时候我们就需要对图片进行压缩或者缩放了&#xff0c;今天教大家如何缩小图片体积 打开智游剪辑&#xff08;官网: zyjj.cc&#xff09;&…

MySQL慢查询怎么办?需要关注Explain的哪些关键字?

目录 1-引言&#xff1a;什么是慢查询1-1 慢查询定义1-2 为什么排查慢查询 2-核心&#xff1a;慢查询排查2-1 慢查询定位2-2 慢查询解决2-2-1 Explain 排查慢查询2-2-2 Explain 重点关键字 3-总结&#xff1a;慢查询知识点小结 1-引言&#xff1a;什么是慢查询 1-1 慢查询定义…

LabelMe数据集格式问题

注意图片的通道数&#xff0c;之前我们都说RGB&#xff0c;但是在这里要看图片位深。 图像是rgba四个通道的&#xff0c;第四个通道是透明通道。 注意png格式的不只是文件名后缀是 .png &#xff0c;也可能是后缀名是 .jpg 但是图片里面的深度是为32的&#xff0c;常规的后缀是…

如何使用JSONB类型在PostgreSQL中存储和查询复杂的数据结构?

文章目录 解决方案1. 创建包含JSONB列的表2. 插入JSONB数据3. 查询JSONB数据4. 创建索引以优化查询性能 示例代码结论 在PostgreSQL中&#xff0c;JSONB是一种二进制格式的JSON数据类型&#xff0c;它允许你在数据库中存储和查询复杂的JSON数据结构。与普通的JSON类型相比&…

Matlab新手快速上手2(粒子群算法)

本文根据一个较为简单的粒子群算法框架详细分析粒子群算法的实现过程&#xff0c;对matlab新手友好&#xff0c;源码在文末给出。 粒子群算法简介 粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;PSO&#xff09;是一种群体智能优化算法&#xff0c;灵感来源于…

flutter 实现表单的封装包含下拉框和输入框

一、表单封装组件实现效果 //表单组件 Widget buildFormWidget(List<InputModel> formList,{required GlobalKey<FormState> formKey}) {return Form(key: formKey,child: Column(children: formList.map((item) {return Column(crossAxisAlignment: CrossAxisAlig…

【BUG】Hexo|GET _MG_0001.JPG 404 (Not Found),hexo博客搭建过程图片路径正确却找不到图片

我的问题 我查了好多资料&#xff0c;结果原因是图片名称开头是_则该文件会被忽略。。。我注意到网上并没有提到这个问题&#xff0c;遂补了一下这篇博客并且汇总了我找到的所有解决办法。 具体检查方式&#xff1a; hexo生成一下静态资源&#xff1a; hexo g会发现这张图片…

配置静态路由实现全网互通

1、实验环境 如图下所示&#xff0c;三台路由器R1&#xff0e;R2&#xff0c;R3两两互连&#xff0c;每台路由器上都配置了Loopback地址模拟网络环境。 2、需求描述 需要在三台路由器上配置静态路由&#xff0c;以实现各网段之间的互通。 若要实现全网互通,必须明确如下两个问…

【GlobalMapper精品教程】075:将影像的颜色赋予点云实现点云真彩色

文章目录 一、加载点云与影像数据二、将影像色彩赋予点云三、保存色彩点云四、注意事项一、加载点云与影像数据 加载本实验数据(data075.rar)中的影像、点云数据,并用Globalmapper提供的卷帘工具(快速浏览图像)查看: 启动卷帘工具,左右拖动实现卷帘效果: 影像VS点云:…