MD5/AES/RSA 替换为国密 SM3/SM4/SM2 实现前后端交互

APP-META / 元宇宙应用平台

基于 Spring Boot3、Vue3、Naive UI 构建,助力应用快速开发、发布、运维的低代码平台,旨在帮助使用者(包含但不限于开发人员、业务人员)快速响应业务需求

背景

目前平台使用的加解密算法为 MD5(信息摘要)AES(对称加密)RSA(非对称加密),出于性能与安全性考虑,我决定更换为对应的国密算法:SM3SM4SM2

算法名称类型备注
SM2非对称加密算法基于ECC,故其签名速度与秘钥生成速度都快于RSA
SM3信息摘要对标 MD5
SM4对称加密密钥长度和分组长度均为128位,类似于 AES/DES

本文将讲述如何实现后端(Java、Spring Boot)、前端(JavaScript)加解密互通😄。

依赖库

这里选择 Bouncy Castle 实现加解密,它是一个广泛使用的加密库,它提供了GM(即国密)支持模块,允许在Java中使用国密算法。这个库是开源的,可以免费用于商业和非商业项目。

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.78.1</version>
</dependency>

后端/Java

类名为:GMUtil,在 JDK21 测试通过

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;public class GMUtil {public static String DEFAULT_ENCODING = "utf-8";private static String SM4 = "SM4";private static String SM3 = "SM3";/*** 加密算法/分组加密模式/分组填充方式* PKCS5Padding-以8个字节为一组进行分组加密* 定义分组加密模式使用:PKCS5Padding*/private static String SM4_TRANSFORM = "SM4/ECB/PKCS5Padding"; // "SM4/CBC/PKCS7Padding";private static String PROVIDER = "BC";private static String SPEC = "sm2p256v1";static {Security.addProvider(new BouncyCastleProvider());}public static String createSm4Key() throws Exception {// 生成 SM4 密钥KeyGenerator keyGen = KeyGenerator.getInstance(SM4, PROVIDER);keyGen.init(128, new SecureRandom()); // SM4 使用 128 位密钥SecretKey secretKey = keyGen.generateKey();return bytesToHex(secretKey.getEncoded());}public static String sm4Encrypt(String content, String key) throws Exception{return sm4Encrypt(content, key, DEFAULT_ENCODING);}/**** @param content* @param hexKey* @param charsetName* @return* @throws Exception*/public static String sm4Encrypt(String content, String hexKey, String charsetName) throws Exception {Cipher cipher = Cipher.getInstance(SM4_TRANSFORM, PROVIDER);byte[] keyBytes = Hex.decode(hexKey);cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SM4));byte[] ciphertext = cipher.doFinal(content.getBytes(charsetName));return bytesToHex(ciphertext);}public static String sm4Decrypt(String encryptText, String hexKey) throws Exception {return sm4Decrypt(encryptText, hexKey, DEFAULT_ENCODING);}/**** @param encryptText* @param hexKey* @param charsetName* @return* @throws Exception*/public static String sm4Decrypt(String encryptText, String hexKey, String charsetName) throws Exception {Cipher cipher = Cipher.getInstance(SM4_TRANSFORM, PROVIDER);byte[] keyBytes = Hex.decode(hexKey);cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SM4));byte[] ciphertext = cipher.doFinal(Hex.decode(encryptText));return new String(ciphertext, charsetName);}/*** 生成文本内容的信息摘要* @param content 文本* @return 返回长度为 64 的 hex 文本*/public static String sm3(String content) throws Exception {// 获取 SM3 MessageDigest 实例MessageDigest digest = MessageDigest.getInstance(SM3, PROVIDER);digest.update(content.getBytes());return bytesToHex(digest.digest());}public static String sm3(InputStream is) throws Exception {// 获取 SM3 MessageDigest 实例MessageDigest digest = MessageDigest.getInstance(SM3, PROVIDER);byte[] buffer = new byte[1024];int numRead;while ((numRead = is.read(buffer)) > 0) {digest.update(buffer, 0, numRead);}is.close();return bytesToHex(digest.digest());}/*** 第一个元素为私钥、第二个元素为公钥、第三个元素为 30 开头的私钥、第四个元素为 30 开头的公钥** @return SM2 密码对* @throws Exception*/public static List<String> createSm2Key() throws Exception {List<String> keys = new ArrayList<>(2);KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC",  PROVIDER);keyPairGen.initialize(new ECGenParameterSpec(SPEC), new SecureRandom());KeyPair keyPair = keyPairGen.generateKeyPair();BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic();BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate();//私钥(16进制字符串,头部不带00长度共64)keys.add(bytesToHex(priKey.getS().toByteArray()));//获取公钥(16进制字符串,头部带04长度共130)keys.add(bytesToHex(((BCECPublicKey)keyPair.getPublic()).getQ().getEncoded(false)));/*同时添加 30 开头的密钥在X.509证书中,公钥信息是按照ASN.1编码规则进行编码的,而这种编码可能会在公钥前面加上一些额外的信息,使得整个序列看起来以“30”开头。“30”在十六进制中对应于ASN.1的SEQUENCE标签,表明这是一个复合数据类型,包含了多个元素,如算法标识符和公钥本身。*/keys.add(bytesToHex(priKey.getEncoded()));keys.add(bytesToHex(pubKey.getEncoded()));return keys;}public static String sm2Encrypt(String content, String hexPubKey) throws Exception {return sm2Encrypt(content, hexPubKey, DEFAULT_ENCODING);}/*** SM2 加密* @param content       待加密文本* @param hexPubKey     公钥串(HEX编码)* @param charsetName   字符集* @return              HEX编码的加密结果* @throws Exception*/public static String sm2Encrypt(String content, String hexPubKey, String charsetName) throws Exception{SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);CipherParameters pubKeyParams;if(hexPubKey.length() == 130 && hexPubKey.startsWith("04")){// 获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName(SPEC);// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数NECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());//提取公钥点ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(hexPubKey));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04pubKeyParams = new ECPublicKeyParameters(pukPoint, domainParameters);}elsepubKeyParams = PublicKeyFactory.createKey(Hex.decode(hexPubKey));engine.init(true, new ParametersWithRandom(pubKeyParams));byte[] bytes = content.getBytes(charsetName);return bytesToHex(engine.processBlock(bytes, 0, bytes.length));}public static String sm2Decrypt(String encryptText, String hexPriKey)throws Exception {return sm2Decrypt(encryptText, hexPriKey, DEFAULT_ENCODING);}/*** SM2 解密* @param encryptText* @param hexPriKey* @param charsetName* @return* @throws Exception*/public static String sm2Decrypt(String encryptText, String hexPriKey, String charsetName) throws Exception {SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);CipherParameters priKeyParams;if(hexPriKey.length() == 64 || (hexPriKey.length() == 66 && hexPriKey.startsWith("00"))){//获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName(SPEC);//构造domain参数ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());BigInteger privateKeyD = new BigInteger(hexPriKey, 16);priKeyParams = new ECPrivateKeyParameters(privateKeyD, domainParameters);}elsepriKeyParams = PrivateKeyFactory.createKey(Hex.decode(hexPriKey));engine.init(false, priKeyParams);byte[] bytes = Hex.decode(encryptText);return new String(engine.processBlock(bytes, 0, bytes.length), charsetName);}public static String hexToBase64(String hex){return bytesToBase64(Hex.decode(hex));}public static String base64ToHex(String base64) throws UnsupportedEncodingException {return bytesToHex(Base64.getDecoder().decode(base64));}/*** 将字节数组转换为十六进制字符串的方法* @param bytes 字节数组* @return*/private static String bytesToHex(byte[] bytes) throws UnsupportedEncodingException {return new String(Hex.encode(bytes), DEFAULT_ENCODING);}private static String bytesToBase64(byte[] bytes){return Base64.getEncoder().encodeToString(bytes);}
}

前端/JavaScript

使用 sm-crypto 库

const crypto = require('sm-crypto')const text = "集成显卡 2024!"
console.debug("明文:", text)console.debug("\nSM3 摘要:", crypto.sm3(text))const sm4Key = "d1d412be2a82dab71873639add228732"
// 使用默认模式(ECB/PKCS5Padding)加密
const sm4Text = crypto.sm4.encrypt(text, sm4Key)
console.debug("\nSM4 加密:", sm4Text)
console.debug("SM4 解密:", crypto.sm4.decrypt(sm4Text, sm4Key))const sm2PubKey = "04e253c9b72d115a3f4e12f9c1c229e3932e2c69ad95e0a083c168954cab1245c7c3324361e1ba8ee177e7af279e4ca528f79656f6e65dbd77e6b9f4708c73f1df"
const sm2PriKey = "00c71cd11365567948ba8b049679ecbd7d7dd31f0109b5bac0aa0dc47494707dd2"// 使用 SM2 加密,使用默认模式 C1C3C2
let encryptData = crypto.sm2.doEncrypt(text, sm2PubKey)
/*** 默认模式解密* 如果是解密来自 Java 端 Bouncy Castle 的密文* 可能需要删除开头的 04 字符*/
let decryptData = crypto.sm2.doDecrypt(encryptData, sm2PriKey)
console.debug("\nSM2 加密:", encryptData)
console.debug("SM2 解密:", decryptData)

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

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

相关文章

LabVIEW设备检修信息管理系统

开发了基于LabVIEW设计平台开发的设备检修信息管理系统。该系统应用于各种设备的检修基地&#xff0c;通过与基地管理信息系统的连接和数据交换&#xff0c;实现了本地检修工位数据的远程自动化管理&#xff0c;提高了设备的检修效率和安全性。 项目背景 现代设备运维过程中信…

Microsoft Edge(简称Edge)

Microsoft Edge&#xff08;简称Edge&#xff09;是一款由微软开发的网页浏览器&#xff0c;它为用户提供了许多便捷的功能和选项。以下是Edge浏览器的使用方法&#xff1a; 一、基本使用方法 打开Edge浏览器&#xff1a; 可以在Windows的开始菜单中找到“Microsoft Edge”并点…

Codeforces Round 958 (Div. 2)

C o d e f o r c e s R o u n d 958 ( D i v . 2 ) \Huge{Codeforces Round 958 (Div. 2)} CodeforcesRound958(Div.2) 文章目录 Problems A. Split the Multiset题意思路标程 Problems B. Make Majority题意思路标程 Problems C. Increasing Sequence with Fixed OR题意思路标…

MySQL 进阶(四)【锁】

1、锁 1.1、锁的概述 锁就不需要多介绍了&#xff0c;多个用户访问共享数据资源&#xff0c;如何保证数据并发访问的一致性、有效性是数据库最重要的问题。同时&#xff0c;锁冲突也是影响一个数据库并发性能最重要的因素。 MySQL 中锁的划分有三类&#xff1a; 全局锁&…

2024-07-12升级问题:Android SDK升级导致 Canvas.FULL_COLOR_LAYER_SAVE_FLAG 等标志位无法使用

Canvas.FULL_COLOR_LAYER_SAVE_FLAG 是一个标志位&#xff0c;用于在 Android 的 Canvas 类中保存画布的颜色层。当使用 saveLayer() 方法时&#xff0c;可以传递这个标志位来指示保存整个颜色层。这样&#xff0c;在恢复画布状态时&#xff0c;颜色层也会被恢复。 工程从Andr…

力扣---46.全排列

给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; 输入&#xff1a;nums …

前后端工作重点小结

前端和后端的区分 前端&#xff08;Frontend&#xff09; 和 后端&#xff08;Backend&#xff09; 是 Web 开发中的两个主要部分&#xff0c;它们有不同的职责和技术栈。 前端&#xff08;Frontend&#xff09; 职责&#xff1a;负责用户界面的呈现和用户交互。主要语言&am…

Qt中While循环等待

QEventLoop的方法&#xff1a; .h文件 #ifndef CONTROLLER_H #define CONTROLLER_H#include <QWidget> #include <QPushButton> #include <QVBoxLayout> #include <QEventLoop> #include <QTimer> #include <QDebug>class Controller : …

如何通过网络快速搜寻到自己的STM32设备

目录 一、问题概述 二、解决思路 三、代码实现 1.创建任务 2.UDP广播接收 一、问题概述 以前一直用RS232串口修改设备配置信息&#xff0c;但是现场施工人员的232线太细&#xff0c;经常容易断掉&#xff0c;这次准备用网口去修改&#xff0c;遇到了一个问题&#xff0c;…

C语言学习笔记[24]:循环语句while②

getchar()的使用场景 int main() {char password[20] {0};printf("请输入密码&#xff1a;");//输入 123456 后回车scanf("%s", passwoed);//数组名本身就是数组地址printf("请确认密码&#xff1a;Y/N");int ch getchar();if(Y ch)printf(&…

区块链学习05-web3中solidity和move语言

Solidity 和 Move 语言的比较&#xff1a;Web3 开发中的两种选择 Solidity 和 Move 都是用于开发区块链平台智能合约的编程语言。它们具有一些相似之处&#xff0c;但也存在一些关键差异。 相似之处: Solidity 和 Move 都是图灵完备语言&#xff0c;这意味着它们可以表达计算…

搜维尔科技:Xsens DOT 可穿戴传感器介绍及示例应用演示

Xsens DOT可穿戴传感器介绍及示例应用演示 搜维尔科技&#xff1a;Xsens DOT 可穿戴传感器介绍及示例应用演示

JavaEE:Spring Web简单小项目实践二(用户登录实现)

学习目的&#xff1a; 1、理解前后端交互过程 2、学习接口传参&#xff0c;数据返回以及页面展示 1、准备工作 创建SpringBoot项目&#xff0c;引入Spring Web依赖&#xff0c;添加前端页面到项目中。 前端代码&#xff1a; login.html <!DOCTYPE html> <html lang&…

关于window配置gitlab和gitee平台共存

今天使用gitlab拉取代码突然提示 gitgitlab.xxx.com: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password). 以为是ssh公钥没有配置好&#xff0c;遂又进行了一番配置&#xff0c;实际上并不是这个问题造成的&#xff0c;但还是想记录一下步骤&#xff0c;以…

<Rust><GUI>rust语言GUI库tauri体验:前、后端结合创建一个窗口并修改其样式

前言 本文是rust语言下的GUI库&#xff1a;tauri来创建一个窗口的简单演示&#xff0c;主要说明一下&#xff0c;使用tauri这个库如何创建GUI以及如何添加部件、如何编写逻辑、如何修改风格等&#xff0c;所以&#xff0c;这也是一个专栏&#xff0c;将包括tauri库的多个方面。…

【前端】原生实现图片的放大与缩放

需求 点击图片&#xff0c;出现一个蒙版和图片&#xff0c;双指捏合可以使图片放大或缩小。 使用前端框架是svelte&#xff0c;但是不影响&#xff0c;功能都是JS实现。 代码 相关定义&#xff1a; import { onMount, afterUpdate, onDestroy } from svelte;let imgDetailRef…

搜维尔科技:Varjo XR-4开箱测评,里面都有啥?

Varjo XR-4开箱测评&#xff0c;里面都有啥&#xff1f; 搜维尔科技&#xff1a;Varjo XR-4开箱测评&#xff0c;里面都有啥&#xff1f;

mysql数据库转PostgreSQL,批量处理xml中的sql,字段加上双引号脚本。

最近在当机器人给大写字段加上双引号,人都麻木了…这么多怎么加,这应该是机器人该干的事,不行动手写脚本,本来用python写的脚本,为了方便同事用,就用js改写了一下,样式不好看,能用就行。 全部代码如下: <!DOCTYPE html> <html lang="zh-CN"> &…

小阿轩yx-zookeeper+kafka群集

小阿轩yx-zookeeperkafka群集 消息队列(Message Queue) 是分布式系统中重要的组件 通用的使用场景可以简单地描述为 当不需要立即获得结果&#xff0c;但是并发量又需要进行控制的时候&#xff0c;差不多就是需要使用消息队列的时候。 消息队列 什么是消息队列 消息(Mes…

【HarmonyOS开发】弹窗交互(promptAction )

实现效果 点击按钮实现不同方式的弹窗showToast showDialog showActionMenu 代码实现 1.引入’ohos.promptAction’ import promptAction from ohos.promptAction;2.通过promptAction 实现系统既定的弹窗 import promptAction from ohos.promptAction;Entry Component st…