JAVA集成国密SM3

2023-11-02

国密算法概述: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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA集成国密SM3 的相关文章

  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • INSERT..RETURNING 在 JOOQ 中不起作用

    我有一个 MariaDB 数据库 我正在尝试在表中插入一行users 它有一个生成的id我想在插入后得到它 我见过this http www jooq org doc 3 8 manual sql building sql statemen
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 如何为俚语和表情符号构建正则表达式 (regex)

    我需要构建一个正则表达式来匹配俚语 即 lol lmao imo 等 和表情符号 即 P 等 我按照以下示例进行操作http www coderanch com t 497238 java java Regular Expression D
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • 使用 JMF 创建 RTP 流时出现问题

    我正处于一个项目的早期阶段 需要使用 RTP 广播DataStream创建自MediaLocation 我正在遵循一些示例代码 该代码目前在rptManager initalize localAddress 出现错误 无法打开本地数据端口
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O

随机推荐