JAVA集成国密SM3

news2024/11/25 8:16:45

JAVA集成国密SM3加密、验签

  • 一、pom配置
  • 二、加密代码集成
    • 2.1、目录结构
    • 2.2、源码
    • 2.3、测试
  • 三、验签代码集成
    • 2.1、目录结构
    • 2.2、源码
    • 2.3、测试
  • 四、相关链接

国密算法概述:https://blog.csdn.net/qq_38254635/article/details/131801527

SM3杂凑算法
SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。

一、pom配置

<!-- 国密 -->
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15to18</artifactId>
	<version>1.66</version>
</dependency>

<!-- 验签 -->
<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.4.5</version>
</dependency>

二、加密代码集成

2.1、目录结构

在这里插入图片描述

2.2、源码

SecretCommon.java

package com.secret.sm3;

import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

/**
 * SM3密码杂凑算法(哈希算法)
 * SM3杂凑算法是我国自主设计的密码杂凑算法。
 * 适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。
 * 为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短。
 * 例如MD5输出128比特杂凑值,输出长度太短,影响其安全性SHA-1算法的输出长度为160比特,SM3算法的输出长度为256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。
 */
public class SecretCommon {

    /**
     * sm3算法加密,不可逆加密
     * @param plainText 需加密的明文字符串
     * @return 加密后固定长度64的16进制字符串
     */
    public static String encrypt(String plainText) {
        return ByteUtils.toHexString(encrypt(plainText.getBytes()));
    }

    /**
     * sm3算法加密,不可逆加密
     * @param plainByte 需加密的明文数组
     * @return 加密后固定长度64的16进制数组
     */
    public static byte[] encrypt(byte[] plainByte) {
        SM3Digest sm3Digest = new SM3Digest();
        sm3Digest.update(plainByte, 0, plainByte.length);
        byte[] digestByte = new byte[sm3Digest.getDigestSize()];
        sm3Digest.doFinal(digestByte, 0);
        return digestByte;
    }

    /**
     * sm3算法通过密钥进行加密,不可逆加密
     * @param keyText 密钥字符串
     * @param plainText 需加密的明文字符串
     * @return 加密后固定长度64的16进制字符串
     */
    public static String encryptByKey(String keyText, String plainText) {
        return ByteUtils.toHexString(encryptByKey(keyText.getBytes(), plainText.getBytes()));
    }

    /**
     * sm3算法通过密钥进行加密,不可逆加密
     * @param keyByte 密钥数组
     * @param plainByte 需加密的明文数组
     * @return 加密后固定长度64的16进制数组
     */
    public static byte[] encryptByKey(byte[] keyByte, byte[] plainByte) {
        KeyParameter keyParameter = new KeyParameter(keyByte);
        SM3Digest sm3Digest = new SM3Digest();
        HMac hMac = new HMac(sm3Digest);
        hMac.init(keyParameter);
        hMac.update(plainByte, 0, plainByte.length);
        byte[] result = new byte[hMac.getMacSize()];
        hMac.doFinal(result, 0);
        return result;
    }

}

Utils.java

package com.secret.sm3;

public class Utils {

    /**
     * sm3算法加密,不可逆加密
     * @param plainText 需加密的明文字符串
     * @return 加密后固定长度64的16进制字符串
     */
    public static String encrypt(String plainText) {
        return SecretCommon.encrypt(plainText);
    }

    /**
     * sm3算法通过密钥进行加密,不可逆加密
     * @param keyByte 密钥字符串
     * @param plainText 需加密的明文字符串
     * @return 加密后固定长度64的16进制字符串
     */
    public static String encryptByKey(String keyText, String plainText) {
        return SecretCommon.encryptByKey(keyText, plainText);
    }

}

测试类:Test.java

package com.secret.sm3;

public class Test {

    public static void main(String[] args) {
        System.out.println("SM3加密:" + Utils.encrypt("I believe you can do anything"));
        System.out.println("SM3秘钥加密:" + Utils.encryptByKey("myKey", "Do what you want to do"));
    }

}

2.3、测试

在这里插入图片描述
使用方法参考测试类即可。

三、验签代码集成

2.1、目录结构

在这里插入图片描述

2.2、源码

SignatureCommon.java

package com.secret.sm3.signature;

import cn.hutool.crypto.SmUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SignatureCommon {

    private static Logger log = LoggerFactory.getLogger(SignatureCommon.class);

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     */
    public static boolean checkSignJson(String sign, Object object, String privateKey, Long timestamp)  {
        return checkSignJson(sign, object, privateKey, timestamp, SignatureConstant.SIGNATURE_VALIDITY_TIME);
    }

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     * @param validityTime 校验时间,单位分钟
     */
    public static boolean checkSignJson(String sign, Object object, String privateKey, Long timestamp, Integer validityTime)  {
        //关闭验签,默认TRUE
        if(!SignatureConstant.SIGNATURE_SWITCH) return true;
        if(!checkTime(timestamp, validityTime)) return false;
        String localSign = getSign(new SignatureStrategyJson().getSignValue(object, privateKey, timestamp));
        return localSign.toUpperCase().equals(sign.toUpperCase());
    }

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     */
    public static boolean checkSignArray(String sign, Object object, String privateKey, Long timestamp) {
        return checkSignArray(sign, object, privateKey, timestamp, SignatureConstant.SIGNATURE_VALIDITY_TIME);
    }

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     * @param validityTime 校验时间,单位分钟
     */
    public static boolean checkSignArray(String sign, Object object, String privateKey, Long timestamp, Integer validityTime) {
        //关闭验签,默认TRUE
        if(!SignatureConstant.SIGNATURE_SWITCH) return true;
        if(!checkTime(timestamp, validityTime)) return false;
        String localSign = getSign(new SignatureStrategyArray().getSignValue(object, privateKey, timestamp));
        return localSign.toUpperCase().equals(sign.toUpperCase());
    }

    /**
     * 加密签名
     * @param signValue 待加密签名字符串
     * @return 加密后签名字符串
     */
    public static String getSign(String signValue){
        return SmUtil.sm3(signValue);
    }

    /**
     * 校验时间范围
     * @param timestamp 页面时间
     */
    public static boolean checkTime(Long timestamp, Integer validityTime) {
        Long nowTime = System.currentTimeMillis();
        Long difference = nowTime - timestamp;
        //配置时间大于0,则验证时间戳,在时间范围内才处理
        if (validityTime > 0){
            if(difference >= 60000 * validityTime || difference <= 0){
                log.error("时间戳异常,非" + validityTime + "分钟内请求,当前时间戳:" + nowTime);
                return false;
            }
        }
        return true;
    }

}

SignatureConstant.java

package com.secret.sm3.signature;

public class SignatureConstant {

    public static final Boolean SIGNATURE_SWITCH = true; //请求签名开关

    @Deprecated
    public static final Boolean SIGNATURE_SWITCH_TRUE = true; //请求签名开

    @Deprecated
    public static final Boolean SIGNATURE_SWITCH_FALSE = false; //请求签名关

    public static final Integer SIGNATURE_VALIDITY_TIME = 1; //请求签名有效时间 默认1分钟

}

SignatureStrategy.java

package com.secret.sm3.signature;

public interface SignatureStrategy {

    String getSignValue(Object object);

    String getSignValue(Object object, String privateKey, Long timestamp);

}

SignatureStrategyArray.java

package com.secret.sm3.signature;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;

public class SignatureStrategyArray implements SignatureStrategy {

    private static Logger log = LoggerFactory.getLogger(SignatureStrategyArray.class);

    @Override
    public String getSignValue(Object object, String privateKey, Long timestamp) {
        return getSignValue(object) + "&privateKey=" + privateKey + "&timestamp=" + timestamp;
    }

    /**
     * 获取实体类拼成的加密字段
     * @param object 参数实体类
     * @return 待加密字符串
     */
    @Override
    public String getSignValue(Object object) {
        Field[] fields = object.getClass().getDeclaredFields();//获取所有属性
        String[][] fieldArray = new String[fields.length][2]; //用二维数组保存  参数名和参数值
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            fieldArray[i][0] = fields[i].getName().toLowerCase(); //获取属性名
            try {
                fieldArray[i][1] = String.valueOf(fields[i].get(object)); //把属性值放进数组
            } catch (IllegalAccessException e) {
                log.error("signature field:" + fields[i].getName() + "Failed to add value");
            }
        }
        fieldArray = doChooseSort(fieldArray); //对参数实体类按照字母顺序排续
        StringBuilder result = new StringBuilder();
        for (String[] field : fieldArray) {//按照签名规则生成待加密字符串
            result.append(field[0]).append("=").append(field[1]).append("&");
        }
        result = new StringBuilder(result.substring(0, result.length() - 1));//消除掉最后的“&”
        log.info("signature Information:" , result.toString());
        return result.toString();
    }

    /**
     * 对二维数组里面的数据进行选择排序,按字段名按abcd顺序排列
     * @param data 未按照字母顺序排序的二维数组
     */
    private String[][] doChooseSort(String[][] data) {//排序方式为选择排序
        int n = data.length;
        for (int i = 0; i < n - 1; i++) {
            int k = i; // 初始化最小值的小标
            for (int j = i + 1; j < n; j++) {
                if (data[k][0].compareTo(data[j][0]) > 0) { //下标k字段名大于当前字段名
                    k = j;// 修改最大值的小标
                }
            }
            // 将最小值放到排序序列末尾
            if (k > i) { //用相加相减法交换data[i] 和 data[k]
                String tempValue ;
                tempValue = data[k][0];
                data[k][0] = data[i][0];
                data[i][0] = tempValue;
                tempValue = data[k][1];
                data[k][1] = data[i][1];
                data[i][1] = tempValue;
            }
        }
        return data;
    }

}

SignatureStrategyJson.java

package com.secret.sm3.signature;

import cn.hutool.json.JSONUtil;

public class SignatureStrategyJson implements SignatureStrategy {

    @Override
    public String getSignValue(Object object, String privateKey, Long timestamp) {
        return getSignValue(object) + "&privateKey=" + privateKey + "&timestamp=" + timestamp;
    }

    /**
     * 获取实体类拼成的加密字段
     * @param object  传入参数实体类
     * @return  待加密字符串
     */
    public String getSignValue(Object object) {
        return JSONUtil.toJsonStr(object);
    }

}

Utils.java

package com.secret.sm3.signature;

public class Utils {

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     */
    public static boolean checkSign(String sign, Object object, String privateKey, Long timestamp)  {
        return SignatureCommon.checkSignJson(sign, object, privateKey, timestamp);
    }

    /**
     * 验签(JSON方式,较简单)
     * @param sign 前端传入签名(可从请求报文头获取)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳(可从请求报文头获取)
     * @param validityTime 校验时间,单位分钟
     */
    public static boolean checkSign(String sign, Object object, String privateKey, Long timestamp, Integer validityTime)  {
        return SignatureCommon.checkSignJson(sign, object, privateKey, timestamp, validityTime);
    }

    /**
     * 获取签名(JSON方式,较简单)
     * @param object 传输实体模型
     * @param privateKey 签名加密私钥
     * @param timestamp 时间戳
     */
    public static String getSignValue(Object object, String privateKey, Long timestamp) {
        return SignatureCommon.getSign(new SignatureStrategyJson().getSignValue(object, privateKey, timestamp));
    }

}

测试类:TestBean.java

package com.secret.sm3.signature;

public class TestBean {
    private String name;
    private String idCard;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getIdCard() {
        return idCard;
    }
    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }
    public TestBean() {

    }
    public TestBean(String name, String idCard) {
        this.name = name;
        this.idCard = idCard;
    }

}

测试类:Test.java

package com.secret.sm3.signature;

public class Test {

    public static void main(String[] args) throws Exception {
        //模拟请求
        TestBean bean = new TestBean("张三", "532143200009129565");
        Long nowTime = System.currentTimeMillis();
        String privateKey = "143s57a7b60841c2";

        String errorSign = "Today is really a beautiful day";
        System.out.println("一.错误签名示例,验签结果:" + Utils.checkSign(errorSign, bean, privateKey, nowTime));

        //2.签名、时间均正确的示例
        String rightSign = Utils.getSignValue(bean, privateKey, nowTime);
        System.out.println("二.正确签名示例,验签结果:" + Utils.checkSign(rightSign, bean, privateKey, nowTime));

        //3.签名正确,时间错误的示例
        Long oldTime = 1672502400000L; //示例时间:2023-01-01 00:00:00
        System.out.println("三.错误时间示例,验签结果:" + Utils.checkSign(rightSign, bean, privateKey, oldTime));
    }

}

2.3、测试

在这里插入图片描述
使用方法参考测试类即可。

四、相关链接

国密算法概述:https://blog.csdn.net/qq_38254635/article/details/131801527

JAVA集成国密SM2:https://blog.csdn.net/qq_38254635/article/details/131810661

JAVA集成国密SM4:https://blog.csdn.net/qq_38254635/article/details/131810715

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

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

相关文章

怎么学习Java数据库连接(JDBC)? - 易智编译EaseEditing

学习Java数据库连接&#xff08;JDBC&#xff09;是掌握Java与数据库交互的关键步骤。以下是学习Java JDBC的一些建议&#xff1a; 先掌握Java基础&#xff1a; 在学习JDBC之前&#xff0c;确保你已经掌握了Java的基本语法、面向对象编程和其他核心概念。这将有助于更好地理解…

干货分享:商城系统开发方式

商城系统是一种为了满足电子商务需求而开发的系统&#xff0c;它能够实现在线购物、支付、订单管理等功能。在当今互联网时代&#xff0c;商城系统的开发方式多种多样。那么&#xff0c;商城系统开发方式有哪些呢&#xff1f; 1、完全独立自主开发 完全独立自主开发是指企业根…

FactoryBean源码解析

文章目录 一、简介二、FactoryBean 接口的方法三、FactoryBean 与 BeanFactory 的区别四、源码解析五、实际应用 一、简介 FactoryBean 是 Spring 框架中的一个接口&#xff0c;用来创建特定类型的 Bean 对象。实现FactoryBean 接口就可以自定义 Bean 对象的创建过程。Factory…

深入浅出如何通过API瞬间搭建亿万商品外贸代购系统PHP系统

什么是淘宝代购 淘宝代购是近年兴起的一种购物模式&#xff0c;是帮国外客户购买中国商品。主要是通过万邦 科技的外贸代购系统&#xff0c;把淘宝、天猫等电商平台的全站商品通过API 接入到你的网站 上&#xff0c;瞬间就可以架设一个有数亿产品的大型网上商城&#xff0c;而…

【Leetcode】687.最长同值路径

一、题目 1、题目描述 给定一个二叉树的 root ,返回 最长的路径的长度 ,这个路径中的 每个节点具有相同值 。 这条路径可以经过也可以不经过根节点。 两个节点之间的路径长度 由它们之间的边数表示。 示例1: 输入:root = [5,4,5,1,1,5] 输出:2示例2: 输入:root …

IT技术岗的面试技巧分享

我们在找工作时,需要结合自己的现状,针对意向企业做好充分准备。作为程序员,你有哪些面试IT技术岗的技巧?你可以从一下几个方向谈谈你的想法和观点。 方向一:分享你面试IT公司的小技巧 1、事先和邀约人了解公司的基本情况,比如公司的行业,规模,研发人员占比等 2、事先和…

甲板上的战舰(力扣)递归 JAVA

给你一个大小为 m x n 的矩阵 board 表示甲板&#xff0c;其中&#xff0c;每个单元格可以是一艘战舰 ‘X’ 或者是一个空位 ‘.’ &#xff0c;返回在甲板 board 上放置的 战舰 的数量。 战舰 只能水平或者垂直放置在 board 上。换句话说&#xff0c;战舰只能按 1 x k&#xf…

华为OD机试真题 Java 实现【最左侧冗余覆盖子串】【2023Q2 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、思路 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

C++-----vector

本期我们来学习C中的vector&#xff0c;因为有string的基础&#xff0c;所以我们会讲解的快一点 目录 vector介绍 vector常用接口 构造函数 sort 迭代器 size&#xff0c;max_size&#xff0c;capacity&#xff0c;empty reserve和resize front和back data insert和…

VUE- 选取本地图片,自定义裁切图片比例 vue-cropper

裁切图片&#xff0c;按照比例裁切&#xff0c;分步骤 1&#xff1a;el-upload选择本地图片&#xff08;分选择本地和上传两步骤&#xff09; 2&#xff1a;在on-change回调方法中拿到el-upload选中的图片&#xff0c;显示在vueCropper上&#xff08;&#xff09;。 2.1&…

为什么互联网网络攻击总是在瞄准金融行业

自从网络时代开始&#xff0c;金融行业一直是最容易被攻击的几个行业之一&#xff0c;它占火伞云相关跟踪攻击尝试的28%&#xff0c;仅次于游戏行业。随着网络犯罪的增长&#xff0c;金融服务业将继续成为网络攻击者的重点目标。 攻击者出于多种原因瞄准金融领域&#xff0c;但…

STM32—CAN通信

文章目录 一、CAN通信简介1.1 CAN简介1.2 CAN协议特点1.3 CAN通信的帧类型1.4 数据帧结构1.5 CAN的位时序1.6 CAN的仲裁功能 二、STM32F1的CAN2.1 bxCAN简介2.2 bxCAN工作模式2.2.1 初始化模式2.2.2 正常模式2.2.3 睡眠模式2.2.4 静默模式2.2.5 环回模式 2.3 位时序和波特率 三…

Spring(一):Spring 的创建和使用

目录 Spring 是什么&#xff1f; 什么是容器&#xff1f; 什么是 IoC&#xff1f; 什么是 IoC&#xff1f; IoC的优点是啥呢&#xff1f; 理解 IoC DI 概念说明 Spring 的创建 创建 Spring 项目 1. 创建⼀个普通 Maven 项⽬。 2. 添加 Spring 框架⽀持&#xff08;s…

谈一谈,Spring Boot 中的 starter 到底是什么 ?

1. 为什么要用Starter? 现在我们就来回忆一下&#xff0c;在还没有Spring-boot框架的时候&#xff0c;我们使用Spring 开发项目&#xff0c;如果需要某一个框架&#xff0c;例如mybatis&#xff0c;我们的步骤一般都是&#xff1a;到maven仓库去找需要引入的mybatis jar包&am…

【UE5 多人联机教程】01-创建主界面

目录 工程地址 步骤 参考链接 工程地址 GitHub - UE4-Maple/C_MP_Lobby: 多人大厅教程项目 步骤 1. 先新建一个工程 2. 将下载的工程中的Plugins目录拷贝到自己新建的工程下 3. 打开工程&#xff0c;新建一个游戏实例 这里命名为“GI_Main” 在项目设置中设置游戏实例类为…

排序算法之快速排序举例详解-python实现3版代码及改进过程

快速排序思想步骤&#xff1a; 1.找到一个基准值key 2.设置2个元素下标i0和jlen-1 3.从后往前找到比key小的数num[j]&#xff0c;从前往后找到比key大的数num[i]&#xff08;这里有个先后顺序&#xff09; 4.交换这两个数&#xff1a;num[i]&#xff0c;num[j] 5.继续重复…

如何在PADS Logic中查找器件

PADS Logic提供类似于Windows的查找功能&#xff0c;可以进行器件的查找。 &#xff08;1&#xff09;在Logic设计界面中&#xff0c;将菜单显示中的“选择工具栏”进行打开&#xff0c;如图1所示&#xff0c;会弹出对应的“选择工具栏”的分栏菜单选项&#xff0c;如图2所示。…

数据库信息速递 MONGODB 6.0 的新特性,更多的查询函数,加密查询,与时序数据集合 (译)...

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到3群&#xff08;共…

Shell脚本函数 实验

Shell 函数 命令序列按照格式写在一起&#xff0c;用函数的方式调用并进行重复使用命令序列。这就是它的核心作用 使用函数可以避免代码重复&#xff0c;函数可以将大的工程分割成若干小的功能模块&#xff0c;提高代码的可读性。 函数的基本格式写法有两种&#xff0c;如下&…

零钱兑换 II(力扣)动态规划 JAVA

给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号整数。 示例…