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

【相关知识】 

加密算法知识相关博文:浅述.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.mfbz.cn/a/558381.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【系统分析师】系统配置与性能评价

文章目录 1、性能指标2、阿姆达尔解决方案3、性能评价方法 1、性能指标 例题 2、阿姆达尔解决方案 大概了解 例题 3、性能评价方法

122.Mit.S081操作系统内核(实验环境搭建)

目录 一、前言 二、实验官网 三、可参考内容 四、qemu介绍 五、环境搭建 1.Linux系统 ubuntu 脚本安装 检测是否安装成功 2.SSH连接工具 3.获取代码 六、搭建成功实例 1.源码目录简析 2.启动xv6 3.远程连接成功示例 一、前言 Mit6.s081 是麻省理工学院面向本…

Antd:在文本框中展示格式化JSON

要想将对象转换为格式化 JSON 展示在文本框中&#xff0c;需要用到 JSON.stringify JSON.stringify 方法接受三个参数&#xff1a; value&#xff1a;必需&#xff0c;一个 JavaScript 值&#xff08;通常为对象或数组&#xff09;要转换为 JSON 字符串。replacer&#xff1a…

商务品牌解决方案企业网站模板 Bootstrap5

目录 一.前言 二.展示 三.下载链接 一.前言 这个网站包含以下内容&#xff1a; 导航栏&#xff1a;主页&#xff08;Home&#xff09;、关于&#xff08;About&#xff09;、服务&#xff08;Services&#xff09;、博客&#xff08;Blog&#xff09;等页面链接。主页部分…

Adipogen—Progranulin (human) ELISA Kit (mAb/mAb)

前颗粒蛋白&#xff08;Progranulin&#xff0c;PGRN&#xff09;是一种富含半胱氨酸的蛋白质&#xff0c;由~ 6kDa大小的颗粒蛋白&#xff08;granulin&#xff0c;GRN&#xff09;组成&#xff0c;具有多功能生物活性&#xff0c;在癌症、炎症、代谢性疾病和神经退行性疾病中…

2024洗地机名牌排行榜:细数最值得买的4大热门款

随着科技的迅速发展&#xff0c;人们的家里纷纷都添置了新的清洁工具——洗地机&#xff0c;它集合了吸、拖、洗于一体&#xff0c;减轻了很多家庭家务的负担&#xff0c;也成为了很多家庭改善清洁体验的新选择。那么市场上的洗地机品牌琳琅满目&#xff0c;我们要如何挑选一款…

教你三招,玩转AI通用大模型ChatGPT

工欲善其事必先利其器&#xff0c;想要高效的用好ChatGPT&#xff0c;首先&#xff0c;让我们从如何与它进行有效的对话开始。要知道&#xff0c;ChatGPT并非简单的问答机器&#xff0c;而是一个可以通过交互学习和适应的智能体。那么&#xff0c;如何让ChatGPT来更好地理解我们…

ruoyi创建子模块

点击项目 -> new -> Module 选择maven模式 构建完成 子项目默认会加入到父项目maven控制在 父项目 pom文件中 dependencyManagement 标签内加入一下代码 新建子模块的名称<!-- 测试--><dependency><groupId>com.safety</groupId><artifact…

vscode+vue开发常用插件整理

前言&#xff1a; vscode新机开发常用插件整理 1、chinese 简体中文配置 2、file-jump 别名跳转&#xff0c;可以把引入的组件&#xff0c;通过ctrl地址名 跳转组件内部 3、Vue Peek&#xff1a;vue项目中的一些配置&#xff0c;安装后&#xff0c;能实现 ctrl组件名 跳转…

JavaEE进阶:基础知识

JavaEE&#xff1a;Java企业开发 Web网站的工作流程 ⽬前用户对PC端应⽤的开发结构模式主要分为C/S和B/S结构. CS即Client/Server&#xff08;客户机/服务器&#xff09;结构. 常⻅的C/S架构的应⽤⽐如QQ&#xff0c;CCTALK&#xff0c;各种⽹络游戏 等等&#xff0c;⼀般需…

【创建型模式】工厂方法模式

一、简单工厂模式 1.1 简单工厂模式概述 简单工厂模式又叫做静态工厂方法模式。 目的&#xff1a;定义一个用于创建对象的接口。实质&#xff1a;由一个工厂类根据传入的参数&#xff0c;动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。 简单工厂模式…

MYSQL之增删改查(上)

前言&#xff1a; 以下是MySQL最基本的增删改查语句&#xff0c;很多IT工作者都必须要会的命令&#xff0c;也 是IT行业面试最常考的知识点&#xff0c;由于是入门级基础命令&#xff0c;所有所有操作都建立在单表 上&#xff0c;未涉及多表操作。 1、“增”——添加数据 1.1…

【简单介绍下R-Tree】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

228 基于matlab的神经网络人脸识别

基于matlab的神经网络人脸识别。 人脸识别以视网膜、 虹膜、 指纹等生物特征的识别作为生物标识符。生物特征识别不很容易伪造、 放错位置。新型脸识别使用的方法 RobustPCA 和径向基函数网络。程序已调通&#xff0c;可直接运行。 228 人脸识别 生物特征识 神经网络 - 小红书 …

【NTN 卫星通信】NTN的信关站应该建在哪些地方

1 概述 3GPP的卫星通信讨论了透传星和再生星两种方式。透传星方式&#xff0c;卫星主要是做为中继存在&#xff0c;基本上不做通信协议的处理。再生星方式&#xff0c;gNodeB的主要功能在卫星上&#xff0c;完成通信协议的主要内容。无论是透传星还是再生星&#xff0c;都需要通…

校园小情书微信小程序源码/社区小程序前后端开源/校园表白墙交友小程序

校园小情书前端代码&#xff0c;好玩的表白墙、树洞、校园论坛&#xff0c;可独立部署&#xff0c;也可以使用我部署的后台服务&#xff0c;毕业设计的好项目。 搭建教程&#xff1a; 一、注册管理后台 1、登录小情书站点进行注册&#xff1a;https://你的域名 2、注册成功…

【JavaEE多线程】线程中断 interrupt()

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;清灵白羽 漾情天…

动态酷黑主页源码

效果图 PC端 &#xff08;移动端不能访问&#xff09; 部分代码 index.html <!DOCTYPE html> <html lang"zh-CN"> <head> <meta http-equiv"X-UA-Compatible" content"IEedge,chrome1"> <meta charset"ut…

java算法day58 | 单调栈part01 ● 739. 每日温度 ● 496.下一个更大元素 I

739. 每日温度 思路&#xff1a; 这道题用暴力求解法会超时。 那我们就要想如何只遍历一遍就能求解出每个位置的下一个更大值在哪呢。 主要的思想就是空间换时间。定义一个单调栈&#xff0c;每次遇到比栈顶元素小的或相等的&#xff0c;直接入栈&#xff0c;遇到比栈顶元素大的…

电学启蒙积木电子方案

东莞市酷得智能科技有限公司是一家集芯片贸易和电子方案定制开发的研发型公司&#xff0c;其中电子积木方案是酷得经营多年的其中一条比较成熟的研发线。电学积木玩具不仅仅是一种娱乐工具&#xff0c;更是一种教育工具&#xff0c;能够在孩子们的成长过程中发挥多方面的积极作…
最新文章