SpringBoot+vue集成sm国密加密解密

文章目录

  • 前言
  • 认识SM2
  • 后端工具类实现
    • 引入依赖
    • 代码实现
      • 工具类:SM2Util
    • 单元测试
      • 案例1:生成服务端公钥、私钥,前端js公钥、私钥
      • 案例2:客户端加密,服务端完成解密
      • 案例3:服务端进行加密(可用于后面前端测试解密操作)
  • 前端vue2实现
    • 工具类构建:sm2.js
    • 页面vue测试
  • 常见报错
    • 1、前端加密出现:TypeError: Cannot read properties of null (reading ‘multiply
    • 2、后端解密出现:InvalidCipherTextException: invalid cipher text
  • 参考文章

前言

本章配套代码:Gitee仓库/demo-exer

  • 说明:前端vue工具类和库在resources目录下。

本章节实现思路:后端基于Hutool开源工具提供的SmUtil来完成国密加解密,前端使用sm-crypto来实现加解密。

后端:

  • 开源 sm工具类库:国密算法工具-SmUtil

前端:

  • 开源库:sm-crypto

认识SM2

认识

SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法。

SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。

随着密码技术和计算机技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。

对比RSA

SM2性能更优更安全:密码复杂度高、处理速度快、机器性能消耗更小
详细参考: https://www.ecaa.org.cn/667.html

SM2RSA
算法结构基本椭圆曲线(ECC)基于特殊的可逆模幂运算
计算复杂度完全指数级亚指数级
存储空间192-256bit2048-4096bit
秘钥生成速度较RSA算法快百倍以上
解密加密速度较快一般
加密长度限制117

后端工具类实现

引入依赖

<!--    引入Hutool依赖    -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-crypto</artifactId><version>5.8.20</version>
</dependency>
<!--    引入Bouncy Castle依赖    -->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk18on</artifactId><version>1.78.1</version>
</dependency>

代码实现

工具类:SM2Util

image-20240916214852887.

package com.changlu.springboot.sm.util;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.springframework.util.StringUtils;import java.security.KeyPair;public class SM2Util {public static KeyPair generateKeyPair() {return SecureUtil.generateKeyPair("SM2");}/*** 加密* @param str* @return*/public static String encrypt(String str, String privateKey, String publicKey) {try {if (checkKeyIsEmpty(privateKey, publicKey)) {SM2 sm2 = new SM2(privateKey, publicKey);sm2.setMode(SM2Engine.Mode.C1C3C2);return sm2.encryptHex(str, KeyType.PublicKey);}return str;} catch (Exception e) {throw new RuntimeException("sm2加密失败" + e);}}/*** 解密* @param str 加密之后的字符串* @param privateKey 私钥* @param publicKey 公钥* @return 原文密码*/public static String decrypt(String str, String privateKey, String publicKey) {try {if (checkKeyIsEmpty(privateKey, publicKey)) {SM2 sm2 = new SM2(privateKey, publicKey);sm2.setMode(SM2Engine.Mode.C1C3C2);return sm2.decryptStr(str, KeyType.PrivateKey);}return str;} catch (Exception e) {throw new RuntimeException("sm2解密失败" + e);}}private static boolean checkKeyIsEmpty(String privateKey, String publicKey) {if (StringUtils.isEmpty(privateKey) || StringUtils.isEmpty(publicKey)) {return false;}return true;}}

单元测试

image-20240916214837775

案例1:生成服务端公钥、私钥,前端js公钥、私钥

package com.changlu.springboot.sm;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.changlu.springboot.sm.util.SM2Util;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.junit.jupiter.api.Test;import java.security.KeyPair;public class SMUtilTest {// 案例1:生成服务端公钥、私钥,前端js公钥、私钥@Testpublic void testDiySM() {String text = "我是一段测试aaaa";// 生成密钥对KeyPair keyPair = SM2Util.generateKeyPair();// 服务器端使用// 生成私钥String privateKey = HexUtil.encodeHexStr(keyPair.getPrivate().getEncoded());// 生成公钥String publicKey = HexUtil.encodeHexStr(keyPair.getPublic().getEncoded());System.out.println("privateKey=>" + privateKey);System.out.println("publicKey=>" + publicKey);// 前端使用// 生成公钥 Q,以Q值做为js端的加密公钥String publicKeyQ = HexUtil.encodeHexStr(((BCECPublicKey) keyPair.getPublic()).getQ().getEncoded(false));System.out.println("公钥Q:"+ publicKeyQ);// 生成私钥 D,以D值做为js端的解密私钥String privateKeyD = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(keyPair.getPrivate()));System.out.println("私钥D:"+ privateKeyD);// 服务端加解密String encodeStr = SM2Util.encrypt(text, privateKey, publicKey);String formatStr = SM2Util.decrypt(encodeStr, privateKey, publicKey);System.out.println("encodeStr=>" + encodeStr);System.out.println("formatStr=>" + formatStr);}}

效果:该单元测试得到的服务端公钥、私钥,前端js公钥、私钥,可用于服务器端、前端使用。

image-20240916214342674


案例2:客户端加密,服务端完成解密

场景:前端客户端使用前端公钥加密后(可用vue中加密函数生成的加密内容),我们在服务器端进行解密。

package com.changlu.springboot.sm;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.changlu.springboot.sm.util.SM2Util;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.junit.jupiter.api.Test;import java.security.KeyPair;public class SMUtilTest {// 生成的一组私钥、公钥进行测试private String privateKey = "308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420057ab3e1e512e970023c16c545289ecf37dd2cb202daa24c42936f21daa061aca00a06082a811ccf5501822da14403420004981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String publicKey = "3059301306072a8648ce3d020106082a811ccf5501822d03420004981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String publicKeyQ = "04981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String privateKeyD = "057ab3e1e512e970023c16c545289ecf37dd2cb202daa24c42936f21daa061ac";// 案例2:客户端加密,服务端完成解密@Testpublic void testDecrypt() {String encodeStr = "04badafbddce5f728fb11c2007f2230b2fcd0ecf019ac4536370c75dc2e222ca696d20033ab8f76965bd1a9e2691b7a6e4e62d71627874cedd6138453444e1868881e69dbcd3ca13818d6db061561fb87da14e061d9d1c82d550322b2e04c60bcca7998ac51059";String formatStr = SM2Util.decrypt(encodeStr, privateKey, publicKey);System.out.println("formatStr=>" + formatStr);}
}

image-20240916214659774


案例3:服务端进行加密(可用于后面前端测试解密操作)

package com.changlu.springboot.sm;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.changlu.springboot.sm.util.SM2Util;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.junit.jupiter.api.Test;
import java.security.KeyPair;public class SMUtilTest {// 生成的一组私钥、公钥进行测试private String privateKey = "308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420057ab3e1e512e970023c16c545289ecf37dd2cb202daa24c42936f21daa061aca00a06082a811ccf5501822da14403420004981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String publicKey = "3059301306072a8648ce3d020106082a811ccf5501822d03420004981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String publicKeyQ = "04981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67";private String privateKeyD = "057ab3e1e512e970023c16c545289ecf37dd2cb202daa24c42936f21daa061ac";// 案例3:服务端进行加密@Testpublic void testEncrypt() {String str = "changlu test test";String encodeStr = SM2Util.encrypt(str, privateKey, publicKey);System.out.println("encodeStr=>" + encodeStr);}}

image-20240916214800810


前端vue2实现

工具类构建:sm2.js

导入依赖:

cnpm i sm-crypto --save

工具类utils目录中创建sm2.js:

image-20240916214949913

import { sm2 } from 'sm-crypto';// 公钥
const PUBLIC_KEY = '04981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67'
const PRIVATE_KEY = '057ab3e1e512e970023c16c545289ecf37dd2cb202daa24c42936f21daa061ac'// 可配置参数
// 1 - C1C3C2;	0 - C1C2C3;  默认为1
const cipherMode = 1//加密
export function doSM2Encrypt(str) {let msg = strif (typeof str !== 'string') {msg = JSON.stringify(str)}// console.log(msg,'加密前')let publicKey = PUBLIC_KEY// 加密结果let encryptData = sm2.doEncrypt(msg, publicKey, cipherMode)//Base64编码 自行选择是否使用//let baseEncode = Base64.encode(encryptData)// 加密后的密文前需要添加04,后端才能正常解密 (不添加04,后端处理也可以)let encrypt = '04' + encryptDatareturn encrypt
}// 解密
export function doSM2DecryptStr(enStr) {let msg = enStrif (typeof enStr !== 'string') {msg = JSON.stringify(enStr)}let privateKey = PRIVATE_KEYlet enval = enStr.substring(2)// 解密结果let doDecrypt = sm2.doDecrypt(enval , privateKey, cipherMode)console.log("doDecrypt=>", doDecrypt)// 解密后类型转换return doDecrypt;
}

页面vue测试

任意找一个vue页面来进行测试。

首先引入工具类:

import { doSM2Encrypt, doSM2DecryptStr } from '@/utils/sm2'

编写测试函数:

export default {created() {//测试编码this.testEncode()},methods: {testEncode() {// 案例1:前端进行加密let str = "123456"let encodeStr = doSM2Encrypt(str)console.log("前端明文加密之后=>", encodeStr)// 案例2:前端自己加密之后的内容进行解密let originStr = doSM2DecryptStr(encodeStr)console.log("前端自行加密,解密之后=>", originStr)// 案例3:服务器端内容加密后进行解密let testEncodeStr = '04c719fa9dff41a22b8119a3f1ba984303d19c295f1f6a6b8196c7a330cf0e9e1830cbd3cd949c49be0681fd2fa9abca3ed14f8f8d4111c552ef7603793c0a2ae344e9072b7dcaefaf5785634a624d4ca7addcf4ab9ff37abe4ec69847ee5e24a65ce74e8a5b1aecdc467d18b36fba22a8e8'originStr = doSM2DecryptStr(testEncodeStr)console.log("服务器端进行加密,解密之后=>", originStr)},
}

image-20240916220322600


常见报错

1、前端加密出现:TypeError: Cannot read properties of null (reading ‘multiply

出现异常:

image-20240916172117381

解决方式:

错误:一开始直接将服务端生成的公钥作为前端的公钥,该公钥的前缀没有04,此时就会报错。

image-20240916181159848

3059301306072a8648ce3d020106082a811ccf5501822d03420004981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67

正确方式:

image-20240916181217277

此时得到的公钥为:

04981070e26f624917f2717bcaadc000c928c91b49c9c218df33260cafa1d2243c2427fd3486884a67d390751ff4956e35466fb4b925a666229b22d36c26267d67

2、后端解密出现:InvalidCipherTextException: invalid cipher text

问题描述:

image-20240916171945532

解决方案:

和问题1一致,一开始前端使用的公钥并不是D值作为js端的解密私钥,此时生成出来的自然在解密端无法解除。


参考文章

[1]. 使用sm2出现报错 “TypeError: Cannot read properties of null (reading ‘multiply‘)”:https://blog.csdn.net/ciwei0605/article/details/125844154

[2]. BC库实现SM2解密时InvalidCipherTextException:https://blog.csdn.net/weixin_43504369/article/details/132739118

[3]. 从零玩转前后端加解密之SM2-sm2:https://www.cnblogs.com/yby6/p/17414766.html

[4]. 适用于前后端的SM2国密加密解密:https://blog.csdn.net/weixin_53021967/article/details/131594733

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

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

相关文章

禹神:一小时彻底搞懂跨域解决方案

1. 浏览器的同源策略 2. 跨域会受到哪些限制 4. CORS 解决 Ajax 跨域问题 exposedHeaders 不加这个&#xff0c;js拿不到这个响应头(浏览器控制台network中能看见&#xff0c;但是js拿不到) 5. JSONP 解决跨域问题 JSOP只能解决get请求 服务端代码 客户端代码 服务端代码升…

卡尔曼滤波中Q和R与噪声的关系

卡尔曼滤波 一种用于估计系统状态的递归滤波器&#xff0c;通过融合传感器测量和系统模型&#xff0c;提供系统状态的最优估计。 Q和R是什么 在卡尔曼滤波中&#xff0c;Q和R分别表示过程噪声和测量噪声的协方差矩阵。 Q Q Q矩阵&#xff08;过程噪声协方差矩阵&#xff09;…

LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路)

LC并联电路在正弦稳态下的传递函数推导&#xff08;LC并联谐振选频电路&#xff09; 本文通过 1.解微分方程、2.阻抗模型两种方法推导 LC 并联选频电路在正弦稳态条件下的传递函数&#xff0c;并通过仿真验证不同频率时 vo(t) 与 vi(t) 的幅值相角的关系。 电路介绍 已知条件…

人工智能和大模型的简介

文章目录 前言一、大模型简介二、大模型主要功能1、自然语言理解和生成2、文本总结和翻译3、文本分类和信息检索4、多模态处理三、大模型的技术特性1、深度学习架构2、大规模预训练3、自适应能力前言 随着技术的进步,人工智能(Artificial Intelligence, AI)和机器学习(Mac…

建设世界一流财务管理体系【数字化顶层设计】【持续更新】

财务管理是企业管理的中心环节&#xff0c;是企业实现基业长青的重要基础和保障。近年来&#xff0c;中央企业认真贯彻落实党中央、国务院决策部署&#xff0c;高度重视财务管理工作&#xff0c;持续优化管理手段&#xff0c;不断创新管理模式&#xff0c;积极应用先进管理工具…

CSS调整背景

一、设置背景颜色 通过 background-color 属性指定&#xff0c;值可以是十六进制 #ffffff&#xff0c;也可以是rgb(0, 255, 255)&#xff0c;或是颜色名称 "red" div {background-color: red; /* 通过颜色名称设置 */background-color: #ff0000; /* 通过十六进制设…

面向对象程序设计之继承(C++)

1.继承的定义 1.1继承的概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有类特性的基础上进⾏扩展&#xff0c;增加⽅法(成员函数)和属性(成员变量)&#xff0c;这样产⽣新的类&#xff0c;称派⽣类。继承 呈现了⾯向…

给虚拟机linux系统安装交叉编译工具链

我们在电脑上写的代码编译生成的是X86架构的二进制文件&#xff0c;只能在X86平台上运行&#xff0c;而开发板是ARM架构因此需要安装交叉编译链工具&#xff0c;这样在电脑上写的代码交叉编译之后生成的是ARM架构的二进制文件。 绿色的字眼是与本文无关的只是这样有助于我们的…

推荐5款AI论文大纲生成器,一键极速生成!

在当今学术研究和写作领域&#xff0c;AI论文大纲生成器的出现极大地提高了写作效率和质量。以下是五款功能强大且全面的AI论文大纲生成器推荐&#xff1a; 一、千笔-AIPassPaper 千笔-AIPassPaper是一款基于深度学习和自然语言处理技术的AI写作助手&#xff0c;旨在帮助用户…

【探索数据结构与算法】希尔排序原理、实现与分析(图文详解)

目录 一、 引言 二、算法思想 三、算法步骤 四、代码实现 五、复杂度 &#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专栏&#xff1a;探索数据结构与算法 一、 引言 希尔排序&#xff08;Shell Sort&#xff09;是插入排序的一种更高效的改进版本&#x…

【Kubernetes笔记】为什么DNS解析会超时?

【Kubernetes笔记】为什么DNS解析会超时&#xff1f; 目录 1 问题背景2 产生后续的问题3 DNS 负缓存工作原理&#xff1a;4 如何解决和缓解 DNS 负缓存 4.1 减小负缓存 TTL4.2 重试机制4.3 减少 Pod 的频繁重启或调度4.4 使用 Headless Service4.5 手动刷新 DNS 缓存 5 总结 …

【电脑组装】✈️从配置拼装到安装系统组装自己的台式电脑

目录 &#x1f378;前言 &#x1f37b;一、台式电脑基本组成 &#x1f37a;二、组装 &#x1f379;三、安装系统 &#x1f44b;四、系统设置 &#x1f440;五、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上篇文章分享了在平时开发的时候遇到的一种项目整合情况&…

15. 三数之和(实际是双指针类型的题目)

15. 三数之和 15. 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以…

支持升降压型、升压、降压、60V的1.2MHz频率LED恒流驱动器LGS63040、LGS63042

前言&#xff1a; 一款支持升降压的LED驱动器。适合单节锂电池使用。当然不仅于此。SOT23-5封装的外形和丝印 特性 宽输入电压、宽输出电压范围&#xff1a;3.0V-60V 支持 PWM 调光及模拟调光 内置 60V/350mΩ低侧金属氧化物半导体场效应晶体管 1.2MHz固定工作频率 逐周期峰值…

面试官问:你在团队中的角色是什么?

面试官问你在团队中的角色是什么&#xff0c;其目的是了解你如何在团队环境中工作&#xff0c;以及你如何看待自己在团队中的定位。他们希望听到你如何与他人协作、你的领导能力或团队合作精神&#xff0c;以及你是否能适应不同的团队角色。 回答这类问题时&#xff0c;你可以…

(南京观海微电子)——GH7006+Boe_6.8_AV068WVU-N10原理介绍

​​​​​​​​​​​​​​​​​​​​​​​​​​​​

07_Python数据类型_集合

Python的基础数据类型 数值类型&#xff1a;整数、浮点数、复数、布尔字符串容器类型&#xff1a;列表、元祖、字典、集合 集合 集合&#xff08;set&#xff09;是Python中一个非常强大的数据类型&#xff0c;它存储的是一组无序且不重复的元素&#xff0c;集合中的元素必须…

Python 课程12-Python 自动化应用

前言 Python 自动化应用 可以帮助开发者节省时间和精力&#xff0c;将重复性、手动操作变为自动化脚本。例如&#xff0c;Python 可以用于自动化处理文件、邮件、生成报表&#xff0c;甚至可以控制浏览器执行复杂的网页操作任务。借助 Python 的强大库和工具&#xff0c;可以轻…

Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解

目录 一、Linux软件包管理器 - yum&#xff08;ubuntu用apt代替yum&#xff09;1、Linux下安装软件的方式2、认识 yum3、查找软件包4、安装软件5、如何实现本地机器和云服务器之间的文件互传 二、Linux编辑器 - vim1、vim 的基本概念2、vim 下各模式的切换3、vim 命令模式各命令…

开题报告的流程

开题报告是学术研究或工程项目开始前的一个重要环节&#xff0c;它标志着项目正式启动。开题报告的流程通常包括以下几个步骤&#xff1a; 1. **选题与立项**&#xff1a; - 确定研究或项目的主题。 - 进行初步的文献调研和市场调查。 - 提出研究或项目的意义、目标和…