当前位置: 首页 > news >正文

springboot 前后端,基于票据+SHA派生密钥+SM4加解密

背景

目的:1、考试查卷,前后端传递加密的答案;2、保证一定的安全性1)每次查卷生成唯一的、任意票据,作为秘钥生成依据;2)每次请求生成票据,存入redis,并设置过期时间,保证该票据仅1次有效(且有有效期);设计:1)前端请求后端生成票据:票据依据 功能模块:用户id:uuid 生成;2)前端拿着票据,请求答案sm4秘钥生成:后端用票据进行 sha256 摘要计算,并截取固定的的长度作为秘钥后端加密答案:使用 hutool 封装的 bouncycastle 进行sm4 加密3)前端解密答案:前端依据票据:使用 crypto-js (sha256)+ sm-crypto (sm4)进行解密注:sha256 也可以改成hash512 + 依据ts 约定开始截取位数,进一步加大破解难度;

一、后端实现

1 maven依赖

这里测试项目用的:JDK17

<!-- hutool - 每个包都引用 -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.38</version>
</dependency><!-- 国密 -->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.81</version>
</dependency>

2 工具类

TicketCryptoUtil

package com.common.util;import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;import java.util.Arrays;/*** 票据相关加密工具类* <p>* 说明:* - 使用 SHA-256 摘要对票据进行密钥派生(取前16字节)* - 使用 SM4 算法(16进制编码)进行加密和解密*/
public class TicketCryptoUtil {/*** 使用 SHA-256 对票据进行密钥派生,生成 SM4 加解密密钥(16字节)** @param ticket 票据字符串* @return 16字节 SM4 密钥*/public static byte[] generateSm4KeyFromTicket(String ticket) {byte[] sha256Bytes = DigestUtil.sha256(ticket);return Arrays.copyOf(sha256Bytes, 16);}/*** 使用指定的SM4密钥加密明文** @param plainText 加密内容* @param sm4Key    sm4密钥* @return 16禁止加密字符串*/public static String sm4Encrypt(String plainText, byte[] sm4Key) {if (StrUtil.isBlank(plainText)) {return plainText;}SM4 sm4 = SmUtil.sm4(sm4Key);return sm4.encryptHex(plainText);}/*** 使用指定的SM4密钥解密密文(16进制编码密文)*/public static String sm4Decrypt(String hexCipher, byte[] sm4Key) {if (StrUtil.isBlank(hexCipher)) {return hexCipher;}SM4 sm4 = SmUtil.sm4(sm4Key);return sm4.decryptStr(hexCipher);}
}

3 工具类测试:

	String str = "你好,中国!";String ticket = "随便的字符串";byte[] sm4Key = TicketCryptoUtil.generateSm4KeyFromTicket(ticket);String encryptHexStr = TicketCryptoUtil.sm4Encrypt(str, sm4Key);log.info("加签后端16进制字符串:" + encryptHexStr);String decryptStr = TicketCryptoUtil.sm4Decrypt(encryptHexStr, sm4Key);log.info("解密后的字符串:" + decryptStr);

测试结果
在这里插入图片描述

二、前端实现

1 安装依赖

前端国密的基本使用,见npm仓库:http://www.npmmirror.com/package/sm-crypto

# 包含各种国际加密的实现
pnpm install crypto-js
# 包含国密加密 实现
pnpm install sm-crypto

2 工具类

common.js

import crypto from 'sm-crypto'
import sha256 from 'crypto-js/sha256.js'
import Hex from 'crypto-js/enc-hex.js'// 下载文件
const common = {/*** 根据票据派生16字节(32个hex字符)SM4密钥* @param {string} ticket* @returns {string} 32位hex字符串,作为sm-crypto的key*/generateSm4KeyFromTicket(ticket) {const hash = sha256(ticket).toString(Hex);return hash.substring(0, 32); // 16字节16*2=32hex字符},/*** 使用指定的SM4密钥,加密明文,返回Base64密文* @param {string} plainText 加密内容* @param {string} sm4Key 32位hex字符串* @returns {string} 16进制密文*/sm4Encrypt(plainText, sm4Key) {if (!plainText || typeof plainText !== 'string') {return '';}if (!sm4Key || typeof sm4Key !== 'string' || sm4Key.length !== 32) {console.warn('SM4加密失败:密钥无效', sm4Key);return '';}try {return crypto.sm4.encrypt(plainText, sm4Key);} catch (err) {console.warn('SM4加密异常:', err);return '';}},/*** 使用指定的SM4密钥,解密16 进制密文,返回明文* @param {string} hexCipher 16 进制字符串* @param {string} sm4Key 32位hex字符串* @returns {string}*/sm4Decrypt(hexCipher, sm4Key) {if (!hexCipher || typeof hexCipher !== 'string') {return '';}if (!sm4Key || typeof sm4Key !== 'string' || sm4Key.length !== 32) {console.warn('SM4解密失败:密钥无效', sm4Key);return '';}try {return crypto.sm4.decrypt(hexCipher, sm4Key);} catch (err) {console.warn('SM4解密异常:', err);return '';}}
};export default common

3 前端请求基本实现

这里设计到方法,仅做概述

<template>......
</template>
<script setup>
import common from "@/utils/common.js";// 1 生成查看答案的票据const {ticket} = await ticketApi.getTicket({userId: 用户id,sysFunction: '系统所属的功能模块'})// 2 根据票据,获取解密需要的密钥const sm4Key = common.generateSm4KeyFromTicket(ticket);// 3 请求 试题答案let questionTempList = ......// 4 解密答案try {questionTempList.forEach(question => {question.correctAnswer = common.sm4Decrypt(question.correctAnswer, sm4Key);});// 4.1 响应式变量尽量一次性修改值,减少资源消耗questionList.value = questionTempList;} catch (err) {ElMessage.error(err);}
</script>
http://www.lryc.cn/news/600623.html

相关文章:

  • 经典IDE之Turbo C
  • 基于MC9S12XEP100的整车控制器(VCU)设计
  • 【Zephyr】Window下的Zephyr编译和使用
  • Redis的数据淘汰策略是什么?有哪些?
  • 资产负债表及其数据获取
  • 【LeetCode 热题 100】79. 单词搜索——回溯
  • 进阶数据结构:用红黑树实现封装map和set
  • element-plus安装以及使用
  • 机器人仿真(2)Ubuntu24.04下RTX5090配置IsaacSim与IsaacLab
  • Java实现大根堆与小根堆详解
  • 【数据结构】栈和队列的实现
  • 基于DataX的数据同步实战
  • 详解力扣高频SQL50题之1141. 查询近30天活跃用户数【简单】
  • STM32-定时器的基本定时/计数功能实现配置教程(寄存器版)
  • 手动开发一个串口调试工具(二):Qt 串口类基本认识与使用
  • ClickHouse高性能实时分析数据库-消费实时数据流(消费kafka)
  • 【Linux系统】理解硬件 | 引入文件系统
  • Kotlin线程同步
  • 高并发微服务限流算法方案对比与实践指南
  • 告别Vite脚手架局限!MixOne Beta测试招募:你的需求,我们来实现
  • 基于 ThinkPHP 开发的垂直化网址导航
  • 深入解析Hadoop如何实现数据可靠性:三副本策略、校验和验证与Pipeline复制
  • 使用Spring Boot创建Web项目
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频语义理解与智能检索进阶(365)
  • 【工程化】浅谈前端构建工具
  • nginx一个域名下部署多套前端项目
  • 机器学习特征工程详解:特征选择与降维(PCA)
  • NLua和C#交互
  • Flask input 和datalist结合
  • VTK交互——ImageClip