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

news2024/11/14 16:16:57

文章目录

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

前言

博主介绍:✌目前全网粉丝3W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

视频平台:b站-Coder长路

本章节相关

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

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

配套视频讲解(b站):https://www.bilibili.com/video/BV12Qt8eoEbS

本章节实现思路:后端基于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/2153207.html

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

相关文章

巴蒂克图案识别系统源码分享

巴蒂克图案识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

安全热点问题

安全热点问题 1.DDOS2.补丁管理3.堡垒机管理4.加密机管理 1.DDOS 分布式拒绝服务攻击&#xff0c;是指黑客通过控制由多个肉鸡或服务器组成的僵尸网络&#xff0c;向目标发送大量看似合法的请求&#xff0c;从而占用大量网络资源使网络瘫痪&#xff0c;阻止用户对网络资源的正…

手把手教你java+selenium数据驱动测试框架搭建与实践

最近在看JavaseleniumTestNgExcel的数据驱动&#xff0c;如何使用TestNg和Excel进行数据驱动测试。我其实是个自动化测试小白&#xff0c;工作之余看看这方面的书&#xff0c;照着敲敲代码&#xff0c;慢慢理解&#xff0c;希望通过自己坚持不懈的努力&#xff0c;在测试这个职…

Python语言基础教程(下)4.0

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

记录一下(goland导入其他包方法编译不了爆红但能正常使用)

在goLand里面新建go文件,里面放了一个方法,首字符也大写了,但是在别的包里面报错显示无法识别,爆红显示,但是项目能正常运行,这里说下我的解决方案 即可解决

软件设计师——操作系统

&#x1f4d4;个人主页&#x1f4da;&#xff1a;秋邱-CSDN博客☀️专属专栏✨&#xff1a;软考——软件设计师&#x1f3c5;往期回顾&#x1f3c6;&#xff1a;C: 类和对象&#xff08;上&#xff09;&#x1f31f;其他专栏&#x1f31f;&#xff1a;C语言_秋邱 一、操作系统…

粘接黑科技标杆专业展会-ASE CHINA 2024 震撼开幕!

2024年9月19日&#xff0c;第27届国际胶粘剂及密封剂展暨第19届国际胶粘带与薄膜展&#xff08;以下简称ASE CHINA 2024&#xff09;在上海新国际博览中心N3-N4-N5馆璀璨揭幕。ASE CHINA作为粘接新材料产业风向标&#xff0c;历经27年的辛苦耕耘&#xff0c;与业界同仁并肩而行…

发布策略说明

发布策略说明 发布策略 区别 标准发布 在部署新版本应用时删除旧版本应用。发布过程中&#xff0c;您的服务会出现短暂中断。 蓝绿发布 应用更新时生成蓝绿两个版本&#xff0c;两个版本互相热备&#xff0c;通过切换路由权重的方式实现不同版本应用的上下线。 该发布策略具…

Moshi: a speech-text foundation model for real time dialogue

视频号 挺神奇的东西 整下来 kyutai-labs/moshi (github.com) git clone https://github.com/kyutai-labs/moshi.git 在线体验 moshi.chat 结束后 点击Download audio Download video 可以下载音频与视频 &#xff08;不过是webm格式&#xff09; 发行版 已上传至资源 小…

Qt (19)【Qt 线程安全 | 互斥锁QMutex QMutexLocker | 条件变量 | 信号量】

阅读导航 引言一、互斥锁1. QMutex&#xff08;1&#xff09;基本概念&#xff08;2&#xff09;使用示例基本需求⭕thread.h⭕thread.cpp⭕widget.h⭕widget.cpp 2. QMutexLocker&#xff08;1&#xff09;基本概念&#xff08;2&#xff09;使用示例 3. QReadWriteLocker、QR…

AI论文写作PPT思维导图PC小程序开发

AI论文写作PPT思维导图PC小程序开发 AI智能PPT功能 一键生成PPT大纲、一键扩写大纲内容、单独扩写某个大纲内容、一键生成内容关键词、单项内容关键词生成、新增大纲项、修改大纲、删除大纲、选择PPT模板、单页模板一键切换、在线编辑模板&#xff1b;支持导出PPTX、JPEG、&am…

nodejs 013:Prect 样式复用(multiple classes)例子

Prect 简单示例 Prect 为使用相同的现代 API 的快速 3kB React 替代方案。代码形式与 React 基本相同。部分语法区别可见 prect-differences-to-react。以下是一个 Prect 简单示例。 Button目录Button.css&#xff1a; .this {display: inline-block;padding: 3px 8px;margi…

执行网络攻击模拟的 7 个步骤

在进攻和防守策略方面&#xff0c;我们可以从足球队和美式足球队身上学到很多东西。球员们会分析对方球队的策略&#xff0c;找出弱点&#xff0c;相应地调整进攻策略&#xff0c;最重要的是&#xff0c;练习、练习、再练习。作为最低要求&#xff0c;网络安全部门也应该这样做…

基于微信的设备故障报修管理系统设计与实现+ssm论文源码调试讲解

2相关技术 2.1微信小程序 小程序是一种新的开放能力&#xff0c;开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播&#xff0c;同时具有出色的使用体验。尤其拥抱微信生态圈&#xff0c;让微信小程序更加的如虎添翼&#xff0c;发展迅猛。 2.2 MYSQL数据…

一文彻底搞懂大模型 - OpenAI o1(最强推理模型)

最近这一两周看到不少互联网公司都已经开始秋招提前批面试了。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些…

举例说明:自然语言处理实战项目

自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;旨在使计算机能够理解、解释和生成人类语言。以下是一些NLP实战项目的示例&#xff1a; 1. 情感分析&#xff08;Sentiment Analysis&#xff09; 项目描述: …

用 HTML + JavaScript DIY 一个渐进式延迟法定退休年龄测算器

为减轻社会和个人因退休年龄变化带来的冲击&#xff0c;近日&#xff0c;全国人民代表大会常务委员会正式发布了关于实施渐进式延迟法定退休年龄的重要决定。 根据该决定&#xff0c;我国将同步启动对男、女职工法定退休年龄的延迟计划。这一调整将采取渐进式的方式进行&#…

09年408考研真题-数据结构

数据结构 10.【2009统考真题】为解决计算机主机与打印机之间速度不匹配的问题&#xff0c;通常设置一个打印数据缓冲区&#xff0c;主机将要输出的数据依次写入该缓冲区&#xff0c;而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是(B&#xff09;。 A.栈 …

unix中如何查询和修改进程的资源限制

一、前言 一个进程在运行时&#xff0c;会用到各种资源&#xff0c;比如cpu的使用时间、内存空间、文件等等。那么&#xff0c;一个进程能够占用多少资源呢&#xff1f;cpu使用的时间有多长&#xff1f;进程空间有多大&#xff1f;能够创建多少个文件&#xff1f;这个就是本文…

数字IC设计\FPGA 职位经典笔试面试整理--基础篇1

注&#xff1a; 资料都是基于网上一些博客分享和自己学习整理而成的 1&#xff1a;什么是同步逻辑和异步逻辑&#xff1f; 同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系。 同步时序 逻辑电路的特点&#xff1a;各触发器的时钟端全部连接在一…