国密SM4算法进行数据的加密、签名和验签、解密

news2024/11/25 2:55:06

文章目录

  • 前言
  • 一、SM4算法介绍
  • 二、生成128位密钥工具类
  • 三、SM4Util工具类
  • 四、测试示例


前言

本文介绍了SM4算法的基本概念、安全性以及在Java中的应用,包括生成16字节密钥、加密、解密、签名和验签的过程,展示了如何在实际开发中使用SM4算法进行信息安全保护。


一、SM4算法介绍

SM4算法是一种分组密码算法,由中国国家密码管理局发布,主要用于商业密码应用。以下是关于SM4算法的一些关键信息:

SM4算法概述

  • 全称:SM4 分组密码算法
  • 用途:适用于无线局域网的安全领域以及其他需要高强度加密的应用场景
  • 分组长度:128位(16字节)
  • 密钥长度:128位(16字节)
  • 结构:采用Feistel网络结构
  • 轮数:32轮迭代
  • 安全性:具有较高的安全性和效率

SM4算法特点

  • 加密和解密:加密和解密算法结构相同,只是使用的轮密钥顺序相反。
  • 密钥扩展:采用32轮非线性迭代结构,每轮需要一个轮密钥。
  • 资源利用率:算法资源利用率高,密钥扩展算法与加密算法可共用。
  • 实现便利性:加密算法流程和解密算法流程一样,只是轮密钥顺序相反,因此无论是软件实现还是硬件实现都非常方便。
  • 操作单位:算法中包含异或运算、数据的输入输出、线性置换等模块,这些模块都是按8位来进行运算的,现有的处理器都能处理。

SM4算法工作原理

  1. 初始化:
    • 生成128位的密钥。
    • 扩展密钥产生32个轮密钥。
  2. 加密过程:
    • 输入128位的明文。
    • 经过32轮迭代,每轮使用一个轮密钥。
    • 输出128位的密文。
  3. 解密过程:
    • 输入128位的密文。
    • 经过32轮迭代,每轮使用一个轮密钥,但是轮密钥的顺序与加密过程相反。
    • 输出128位的明文。

SM4算法的应用

  • 无线局域网:SM4算法广泛应用于WLAN(无线局域网)的安全通信。
  • 其他应用场景:由于其高效性和安全性,SM4也被用于其他需要加密的应用场景,如数据加密、文件加密等。

SM4算法的优势

  • 高性能:SM4算法设计简洁高效,适合在各种计算平台上实现。
  • 安全性:SM4算法经过严格的评估,被认为具有很高的安全性。
  • 标准化:作为国家标准,SM4算法得到了广泛的接受和支持。

SM4算法的局限性

  • 密钥管理:信息安全取决于对密钥的保护,密钥泄漏意味着任何人都能通过解密密文获得明文。
  • 适用范围:加密算法与解密算法均使用相同的密钥,这意味着在某些特定的应用场景下,SM4算法的适用范围可能受到限制。

二、生成128位密钥工具类

package com.grafana.log;

import java.security.SecureRandom;

public class KeyGenerator {

    /**
     * 生成一个128位(16字节)的随机密钥。
     *
     * @return 生成的128位密钥字节数组。
     */
    public static byte[] generate128BitKey() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] key = new byte[16]; // 16 bytes for 128 bits
        secureRandom.nextBytes(key);
        return key;
    }
}

SM4算法要求密钥是128位,这里笔者提供一个生成随机128位密钥字节数组的工具,实际开发中,可生成一次后转base64保存起来,使用时再解码转成字节数组。


三、SM4Util工具类

pom依赖如下:

<dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcprov-jdk15on</artifactId>
     <version>1.70</version>
</dependency>
package com.grafana.log;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Arrays;

public class Sm4Util {

    private static final String ALGORITHM = "SM4";
    private static final String TRANSFORMATION_ECB = ALGORITHM + "/ECB/PKCS7Padding";
    private static final String TRANSFORMATION_CBC = ALGORITHM + "/CBC/PKCS7Padding";
    private static final String TRANSFORMATION_ECB_NoPADDING = ALGORITHM + "/ECB/NoPadding";
    private static final String TRANSFORMATION_CBC_NoPADDING = ALGORITHM + "/CBC/NoPadding";


    private static final String ALGORITHM_HMAC_SM4 = "HmacSHA256";
    private static final String PROVIDER_NAME = "BC";


    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 签名
     */
    public static byte[] sign(byte[] key, byte[] data) throws Exception {
        Mac mac = Mac.getInstance(ALGORITHM_HMAC_SM4, PROVIDER_NAME);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_HMAC_SM4);
        mac.init(secretKeySpec);
        return mac.doFinal(data);
    }

    /**
     * 验证签名
     */
    public static boolean verify(byte[] key, byte[] data, byte[] signature) throws Exception {
        Mac mac = Mac.getInstance(ALGORITHM_HMAC_SM4, PROVIDER_NAME);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_HMAC_SM4);
        mac.init(secretKeySpec);
        byte[] computedSignature = mac.doFinal(data);
        return Arrays.equals(computedSignature, signature);
    }

    /**
     * /CBC/PKCS7Padding 加密
     */
    public static byte[] encryptByCbcPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        // CBC模式需要一个初始化向量
        byte[] iv = new byte[16]; // 随机生成或指定
        Arrays.fill(iv, (byte) 0x00); // 示例中填充为0
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/PKCS7Padding 解密
     */
    public static byte[] decryptByCbcPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16]; // 随机生成或指定
        Arrays.fill(iv, (byte) 0x00); // 示例中填充为0
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/PKCS7Padding 加密
     */
    public static byte[] encryptByEcbPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/PKCS7Padding 解密
     */
    public static byte[] decryptByEcbPkcs7Padding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/NoPadding 加密
     */
    public static byte[] encryptByEcbPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /ECB/NoPadding 解密
     */
    public static byte[] decryptByEcbPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/NoPadding 加密
     */
    public static byte[] encryptByCbcPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * /CBC/NoPadding 解密
     */
    public static byte[] decryptByCbcPkcs7PaddingNoPadding(byte[] key, byte[] data) throws Exception{
        Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC_NoPADDING , "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }


}

主要功能

  1. HMAC签名:
    • sign: 使用HMAC-SHA256算法生成签名
    • verify: 验证给定的签名是否正确。
  2. 加密和解密:
    • 支持两种工作模式:ECB(电子密码本)和CBC(密码块链接)。
    • 支持两种填充方式:PKCS7Padding 和 NoPadding。
    • 提供了加密和解密的方法组合,例如:
      • /ECB/PKCS7Padding 加密和解密。
      • /CBC/PKCS7Padding 加密和解密。
      • /ECB/NoPadding 加密和解密。
      • /CBC/NoPadding 加密和解密。

代码分析

  1. 常量定义:
    • ALGORITHM: 定义SM4算法名称。
    • TRANSFORMATION_ECB: ECB模式下的SM4加密变换。
    • TRANSFORMATION_CBC: CBC模式下的SM4加密变换。
    • TRANSFORMATION_ECB_NoPADDING: ECB模式下没有填充的SM4加密变换。
    • TRANSFORMATION_CBC_NoPADDING: CBC模式下没有填充的SM4加密变换。
    • ALGORITHM_HMAC_SM4: HMAC签名算法名称,这里实际上是HMAC-SHA256,因为Bouncy Castle不支持HmacSM4。
    • PROVIDER_NAME: Bouncy Castle Provider的名称。
  2. 静态初始化块:
    • Security.addProvider(new BouncyCastleProvider());: 添加Bouncy Castle Provider到JVM的安全提供者列表中。
  3. 加密和解密方法:
    • 每种加密和解密方法都使用Cipher类进行操作。
    • 方法参数包括密钥和数据。
    • 对于CBC模式,还需要初始化向量(IV)。
    • 使用SecretKeySpec和IvParameterSpec创建密钥和IV规格。
    • 使用Cipher.init方法初始化加密或解密操作。
    • 使用Cipher.doFinal方法执行加密或解密操作。
  4. HMAC签名方法:
    • 使用Mac类进行HMAC签名操作。
    • 方法参数包括密钥和数据。
    • 使用Mac.init方法初始化签名操作。
    • 使用Mac.doFinal方法生成签名。

四、测试示例

package com.grafana.log;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Sm4UtilTest {

    private static final byte[] KEY = KeyGenerator.generate128BitKey(); // 16字节128位的密钥
    private static final String DATA = "This is a test message for SM4 encryption.";
    //16字节或16字节的整数倍字符串,无填充模式NoPadding要求
    private static final String DATA16 = "HelloWorldYesYes";

    @Test
    public void testEncryptDecryptEcbPkcs7Padding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByEcbPkcs7Padding(KEY, DATA.getBytes());
        //签名
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        //验证
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByEcbPkcs7Padding(KEY, encrypted);
        System.out.println("解密后数据"+new String(decrypted));
    }

    @Test
    public void testEncryptDecryptCbcPkcs7Padding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByCbcPkcs7Padding(KEY, DATA.getBytes());
        //签名
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        //验证
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByCbcPkcs7Padding(KEY, encrypted);
        System.out.println("解密后数据"+new String(decrypted));
    }


    @Test
    public void testEncryptDecryptEcbPkcs7PaddingNoPadding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByEcbPkcs7PaddingNoPadding(KEY, DATA16.getBytes());
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByEcbPkcs7PaddingNoPadding(KEY, encrypted);
        System.out.println("解密后数据"+new String(decrypted));
    }

    @Test
    public void testEncryptDecryptCbcPkcs7PaddingNoPadding() throws Exception {
        byte[] encrypted = Sm4Util.encryptByCbcPkcs7PaddingNoPadding(KEY, DATA16.getBytes());
        byte[] signatureData = Sm4Util.sign(KEY, encrypted);
        boolean isValid = Sm4Util.verify(KEY, encrypted, signatureData);
        System.out.println("签名验证结果:" + isValid);
        byte[] decrypted = Sm4Util.decryptByCbcPkcs7PaddingNoPadding(KEY, encrypted);
        System.out.println("解密后数据"+new String(decrypted));
    }
}

在测试代码中分别对工具类中的四种方式的加密、签名、验签、解密进行了演示,演示结果依次如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:SM4Util工具类中后两个不需要填充的,要求加密的数据块大小必须是16字节的整数倍,否则会报错,实际开发中中前两种用的比较多。

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

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

相关文章

enq: HW - contention事件来啦

业务系统反应数据库慢&#xff0c;根据时间查看awr报告。 先看一眼事件名称 HW enqueue 用于序列化超出段高水位线的空间分配。如果同时向对象添加大量数据&#xff0c;则多个进程可能同时尝试在高水位线上方分配空间&#xff0c;从而导致争用。 既然是控制资源并发的enq&…

WiFi Analyzer:你的开源WIFI管家!【送源码】

无论是在家里还是在公司&#xff0c;手机连接 WiFi 时&#xff0c;总是会出现 WiFi 信号比较弱&#xff0c;网速变慢的情况。如何检测 WiFi 信号的情况呢&#xff1f;今天给大家推荐一个开源项目——WiFi Analyzer。 项目简介 WiFi Analyzer 是一个开源的 Android 应用程序&a…

母带混音插件-Musik Hack Master Plan 1.59 WiN-MAC,长期更新持续有效

Musik Hack Master Plan 1.59 WiN-MAC 一款专业的音频母带制作流程&#xff0c;只需简单的控制就能制作出适合发布的母带&#xff1a; 水晶般清晰的响度、丰富的模拟饱和度、相位一致的成像、物理磁带模拟&#xff0c;以及修复和监听混音的额外工具。 一。Musik Hack Master P…

在 PhpStorm 中为 .java 文件启用语法高亮,需要正确配置文件类型和关联语言。

点击访问我的技术博客https://ai.weoknow.comhttps://ai.weoknow.com 因为我同时使用java和php混编所以在一个项目中如果同时打开IntelliJ IDEA和PhpStorm不符合我完美主义的本性。 捣鼓了一下搞定了 1. 添加文件类型关联 将 .java 文件与 Java 语言支持关联&#xff1a; …

新160个crackme - 023-TraceMe

运行分析 需破解用户名和序列号 PE分析 C程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida打开&#xff0c;由main函数向下分析&#xff0c;找到DialogFunc函数&#xff0c;并找到关键判断函数sub_401340 进入sub_401340函数&#xff0c;发现算法 发现byte_405…

xxl-job源码学习笔记

文章目录 一、简介二、下载源码三、模块介绍四、源码解析4.1、调度中心启动流程&#xff08;xxl-job-admin&#xff09;4.1.1、JobTriggerPoolHelper&#xff08;触发任务执行的核心组件&#xff09;4.1.2、JobRegistryHelper&#xff08;维护和更新调度中心与执行器之间的注册…

常见中间件漏洞(四、Apache合集)

目录 四、Apache 4.1 CVE-2021-41773 漏洞简介 影响版本 环境搭建 漏洞复现 四、Apache 4.1 CVE-2021-41773 Apache HTTP Server 路径穿越漏洞 漏洞简介 该漏洞是由于Apache HTTP Server 2.4.49版本存在目录穿越漏洞,在路径穿越目录<Directory/>Require all gra…

Spring Boot 依赖之 lombok的@Data注解

Spring Boot 依赖之 lombok的Data注解 编译之后的代码 Java源代码 引入lombok 一、创建 Spring Boot 项目 启动 IntelliJ IDEA&#xff0c;点击 File -> New -> Project...在新项目对话框中&#xff0c;选择 Spring Initializr&#xff0c;点击 Next配置 Spring Ini…

RF放大器(传输线+终止传输线+奥品电路中常见问题+调谐放大器)

2024-8-6&#xff0c;星期二&#xff0c;22:57&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;晴。下班抽出点时间看看书&#xff0c;话不多说&#xff0c;学习开始啦。 今日继续学习模电自选教材的第六章&#xff0c;多级放大器、RF放大器和功率放大器。主要学习…

机械学习—零基础学习日志(高数23——无穷小运算)

零基础为了学人工智能&#xff0c;真的开始复习高数 这段时间&#xff0c;把张宇老师讲解考研的第一部分基本全部学习完毕了。 这里把第一部分的内容最后汇总一下。 无穷小运算——吸收律 这里展示一些无穷小的具体计算思路 无穷小运算——计算方法 泰勒展开的原则 夹逼准则…

C语言程序设计-[6] if语句分支结构

if语句分支结构有三种形式&#xff0c;分别按照语句形式、流程图表示、示例的步骤进行讲解。 1、if语句的一般形式 ——简单if语句 语句形式&#xff1a;if(表达式) 语句1&#xff1b; 执行过程&#xff1a; 如果表达式值为“真”,执行该语句1&#xff0c;然后执行if的下一…

【生成式人工智能-四-chatgpt的训练过程-pretrain预训练自督导式学习督导式学习】

大模型是怎么被训练出来的具有人类智慧的 阶段一训练-自我学习-具备知识训练资料self-supervised learning&#xff08;自督导式学习&#xff09; 阶段二-怎么让模型具备人的智慧supervised learning 督导式学习预训练pretrain为什么要用预训练的模型&#xff1f;Adapter逆向工…

【大专生学前端】:挑战与机遇并存——你怎么看?

在当今互联网飞速发展的时代&#xff0c;前端开发作为连接用户与产品的重要桥梁&#xff0c;一直备受关注。然而&#xff0c;近期社会上流传着一种说法&#xff1a;“大专生学前端&#xff0c;没有前途了&#xff0c;毕业即失业。”这一观点不仅引发了广泛的讨论&#xff0c;也…

【爬虫实战】利用代理爬取电商数据

文章目录 前言工具介绍实战获取网站数据编写代码数据展示 推荐总结 前言 当今电商平台正经历着快速的转型与升级。随着技术的进步和用户需求的多样化&#xff0c;电商不仅从简单的在线购物演变为综合性的购物生态系统&#xff0c;还融合了人工智能、大数据和云计算等先进技术。…

freeRTOS入门学习-基于STM32F103C8T6最小系统板-使用cubeMX创建一个新的工程

一、准备工作 首先打开我们的cubeMX软件&#xff0c;搜索我们对应的MCU&#xff0c;我这里使用的是c8t6最小系统板 接下来双击我们需要的芯片型号 二、System Core配置 进入界面后我们先进行时钟的配置 将HSE配置为晶振 然后我们将SYS配置成为serial wire&#xff0c; …

【单片机毕业设计选题24102】-基于STM32和阿里云的禽舍环境监测控制系统

系统功能: 系统分为主机端和从机端&#xff0c;主机端主动向从机端发送信息和命令&#xff0c;从机端 收到主机端的信息后回复温湿度氨气浓度和光照强度等信息。 从机端操作&#xff1a; 从机端上电后显示“欢迎使用环境监测系统请稍后”两秒后显示第一页面。 从机端口上电…

每日学习笔记:C++ STL算法之数值算法

目录 算法头文件 运算后产生结果 对容器所有元素(数列)进行某种运算&#xff1a; accumulate(beg, end, initValue) accumulate(beg, end, initValue, op) 计算两数列的内积&#xff1a; inner_product(beg1, end1, end2, initValue) inner_product(beg1, end1, end2…

红酒与奶酪:欧洲风情的整合

在欧洲的浪漫风情中&#xff0c;红酒与奶酪总是携手相伴&#xff0c;它们各自不同的魅力交织在一起&#xff0c;仿佛一首悠扬的交响曲&#xff0c;在味蕾上奏响。当洒派红酒&#xff08;Bold & Generous&#xff09;与精选奶酪相遇&#xff0c;一场欧洲风情的整合即将上演。…

亚马逊自发货erp,自动虚拟发货自动生成订单

亚马逊自发货 ERP 自动化虚拟发货&#xff0c;贴牌定制独立部署。 大家好&#xff0c;今天来讲一下 ERP 的虚拟自动发货&#xff01; 1. 以为发货都是人工手动去发货的&#xff0c;其实不然&#xff0c;很多产品一爆就是几十、一百单&#xff0c;不可能一个一个点的去发货&am…

vue3 ResizeObserver如何监听一个容器盒子div宽度高度发生改变

ResizeObserver定义 ResizeObserver&#xff1a;是一个 JavaScript API&#xff0c;用于监视元素的大小变化。它可以观察一个或多个 DOM 元素&#xff0c;以便在元素的大小或形状发生变化时触发回调函数。 ResizeObserver &#xff1a;为了更有效地处理元素尺寸变化而引入的&am…