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

news2024/12/22 23:42:52

文章目录

  • 前言
  • 认识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公钥、私钥
    @Test
    public 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:客户端加密,服务端完成解密
    @Test
    public 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:服务端进行加密
    @Test
    public 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 = str
    if (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' + encryptData
    return encrypt
}

// 解密
export function doSM2DecryptStr(enStr) {
    let msg = enStr
    if (typeof enStr !== 'string') {
      msg = JSON.stringify(enStr)
    }
    let privateKey = PRIVATE_KEY
    let 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.coloradmin.cn/o/2141643.html

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

相关文章

Linux下root用户共享conda环境给其他用户

首先可以先用命令查看环境存储位置 conda env list 比如我的root用户的base环境 # conda environments: # base * /usr/local/miniconda3 在root下先给环境添文件夹加普通用户的权限 chmod -R 755 /usr/local/miniconda3 接下来新建一个用户&#xff0…

基于springboot酒店管理系统

酒店管理系统 552酒店管理系统 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;…

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

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

记录一题---位示图

注意题目条件:盘块号、字号、位号到底是从0开始还是从1开始 盘块号、字号、位号从0开始&#xff0c;若n表示字长&#xff0c;则&#xff1a; (字号,位号)(i,j)的二进制位对应的盘块号bnij b号盘块对应的字号ib/n&#xff0c;位号jb%n 若字号、位号从1开始&#xff0c;n表示字长…

纯小白安装pytorch(快速上手)

1.首先进入你的虚拟环境(不进入也没关系) 在anaconda prompt中打开&#xff0c;输入activate 虚拟环境2.查看自己的conda源 conda config --show channels3.清空自己的conda源 conda config --remove-key channels4.添加源 conda config --add channels https://mirrors.tu…

Matlab simulink建模与仿真 第十六章(用户定义函数库)

参考视频&#xff1a;simulink1.1simulink简介_哔哩哔哩_bilibili 一、用户定义函数库中的模块概览 注&#xff1a;MATLAB版本不同&#xff0c;可能有些模块也会有差异&#xff0c;但大体上区别是不大的。 二、Fcn/Matlab Fcn模块 1、Fcn模块 双击Fcn模块&#xff0c;在对话…

JavaScript 事件处理

一、简介 ​ 事件&#xff1a;发生在HTML元素上的事情&#xff0c;可以是用户的行为&#xff0c;也可以是浏览器的行为&#xff0c;如 用户点击了某个HTML元素用户将鼠标移动到某个HTML元素上用户输入数据时光标离开页面加载完成 ​ 事件源&#xff1a;事件触发的源头&#xf…

【课程系列12】某客时间AI大模型微调训练营

某客时间AI大模型微调训练营 链接 百度网盘地址&#xff1a;https://pan.baidu.com/s/1ZC-fOC_QQjNM6wyVjQcYOg 课程内容 https://u.geekbang.org/subject/finetuning?utm_sourceu_nav_web&utm_mediumu_nav_web&utm_termu_nav_web 深度解读 AI 发展四轮浪潮 技术浪…

PMP--一模--解题--111-120

文章目录 10.沟通管理111、 [单选] 干系人感觉他们收到的信息十分复杂&#xff0c;难以理解&#xff0c;因此&#xff0c;不能正确做出决定。若要解决这个问题&#xff0c;应该怎么做&#xff1f; 14.敏捷--角色--产品负责人PO–职责–1.创建待办列表并排序;2.确认工作优先顺序…

【网络】TCP/IP 五层网络模型:数据链路层

文章目录 认识以太网mac 地址mac 地址和 IP 地址的区别 类型ARP 协议 MTU 日常开发主要是应用层&#xff0c;传输层和应用层密切交互&#xff0c;传输层用到的也不少。网络层以及再往下&#xff0c;日常开发就涉及到的非常少了 认识以太网 插网线的上网方式 80211 是无线 WiF…

Encountered error while trying to install package.> lxml

Encountered error while trying to install package.&#xff1e; lxml 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城…

代码管理系统简介与部署(Introduction and Deployment of Code Management System)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

TCP.IP四层模型

一、TCP/IP模型协议分层 1、应用层&#xff1a; 2、传输层&#xff1a; TCP&#xff1a;传输控制协议 UDP&#xff1a;用户数据报协议 3、网络层&#xff1a; IP: 国际协议&#xff08;IP地址&#xff09; ICMP&#xff1a; 互联网控制消息协议&#xff08;互联网…

dedecms(四种webshell姿势)、aspcms webshell漏洞复现

一、aspcms webshell 1、登陆后台&#xff0c;在扩展功能的幻灯片设置模块&#xff0c;点击保存进行抓包查看 2、在slideTextStatus写入asp一句话木马 1%25><%25Eval(Request(chr(65)))%25><%25 密码是a&#xff0c;放行&#xff0c;修改成功 3、使用菜刀工具连…

【Python笔记】PyCharm大模型项目环境配置

一、PyCharm创建新项目 二、更新pip版本 ...>python.exe -m pip install --upgrade pip 三、生成所需requirements配置文件 ...>pip freeze > requirements.txt 四、安装所需组件requirements.txt ...>pip install -r requirements.txt

算法入门-贪心1

第八部分&#xff1a;贪心 409.最长回文串&#xff08;简单&#xff09; 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回通过这些字母构造成的最长的回文串 的长度。 在构造过程中&#xff0c;请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串…

Linux下文件下载中文乱码问题

最近做的一个项目中&#xff0c;本地打包到线上后&#xff0c;发现生成的文件中出现中文乱码&#xff0c;但在本地运行正常。经排查&#xff0c;文件输入输出流都指定了utf-8的编码格式&#xff0c;IDE的File Encodings也都是utf-8&#xff0c;Linux编码格式也是utf-8&#xff…

JS基础之【对象详解 -- 对象的属性与方法、遍历对象与内置对象】

&#x1f680; 个人简介&#xff1a;某大型国企高级前端开发工程师&#xff0c;7年研发经验&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码…

【数据结构与算法 | 灵神题单 | 自顶向下DFS篇】力扣1022,623

1. 力扣1022&#xff1a;从根到叶的二进制之和 1.1 题目&#xff1a; 给出一棵二叉树&#xff0c;其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高有效位开始的二进制数。 例如&#xff0c;如果路径为 0 -> 1 -> 1 -> 0 -> 1&#xff0c;那…

如何在kotlin中给空字符串(””)和null值设置默认值问题?

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…