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

项目安全-----加密算法实现

目录

对称加密算法

AES (ECB模式)

AES(CBC 模式)。

非对称加密


对称加密算法

对称加密算法,是使用相同的密钥进行加密和解密。使用对称加密算法来加密双方的通信的话,双方需要先约定一个密钥,加密方才能加密,接收方才能 解密。常用的加密算法,有 DES、3DES 和 AES,国密算法包括SM1,SM4和SM7。 目前,使用 DES 来加密数据非常不安全。因此,在业务代码中要避免使用 DES 加密。而 3DES 算法,是使用不同的密钥进行三次 DES 串联调用,虽然解决 了 DES 不够安全的问题,但是比 AES 慢,也不太推荐。我们来看看AES的算法,AES 算法有ECB、CBC、 CFB、OFB、CTR 模式

AES (ECB模式)

private static final String KEY = "secretkey1234567"; //密钥//测试ECB模式@GetMapping("ecb")public void ecb() throws Exception {Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");test(cipher, null);}//获取加密秘钥帮助方法private static SecretKeySpec setKey(String secret) {return new SecretKeySpec(secret.getBytes(), "AES");}//测试逻辑private static void test(Cipher cipher, AlgorithmParameterSpec parameterSpec) throws Exception {//初始化Ciphercipher.init(Cipher.ENCRYPT_MODE, setKey(KEY), parameterSpec);//加密测试文本System.out.println("一次:" + Hex.encodeHexString(cipher.doFinal("abcdefghijklmnop".getBytes())));//加密重复一次的测试文本System.out.println("两次:" + Hex.encodeHexString(cipher.doFinal("abcdefghijklmnopabcdefghijklmnop".getBytes())));//下面测试是否可以通过操纵密文来操纵明文 //发送方账号byte[] sender = "1000000000012345".getBytes();//接收方账号byte[] receiver = "1000000000034567".getBytes();//转账金额byte[] money = "0000000010000000".getBytes();//加密发送方账号System.out.println("发送方账号:" + Hex.encodeHexString(cipher.doFinal(sender)));//加密接收方账号System.out.println("接收方账号:" + Hex.encodeHexString(cipher.doFinal(receiver)));//加密金额System.out.println("金额:" + Hex.encodeHexString(cipher.doFinal(money)));//加密完整的转账信息byte[] result = cipher.doFinal(ByteUtils.concatAll(sender, receiver, money));System.out.println("完整数据:" + Hex.encodeHexString(result));//用于操纵密文的临时字节数组byte[] hack = new byte[result.length];//把密文前两段交换System.arraycopy(result, 16, hack, 0, 16);System.arraycopy(result, 0, hack, 16, 16);System.arraycopy(result, 32, hack, 32, 16);cipher.init(Cipher.DECRYPT_MODE, setKey(KEY), parameterSpec);分区 业务常见问题 的第 16 页  cipher.init(Cipher.DECRYPT_MODE, setKey(KEY), parameterSpec);//尝试解密System.out.println("原始明文:" + new String(ByteUtils.concatAll(sender, receiver, money)));System.out.println("操纵密文:" + new String(cipher.doFinal(hack)));}

 两个相同明文分组产生的密文,就是两个相同的密文分组叠在一起。在不知道密钥的情况下,我们操纵密文实现了对明文数据的修改,对调了发送方账号 和接收方账号。所以说,ECB 模式虽然简单,但是不安全,不推荐使用。

AES(CBC 模式)。

private static final String initVector = "abcdefghijklmnop"; //初始化向量
@GetMapping("cbc")
public void cbc() throws Exception {Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));test(cipher, iv);
}

可以看到,相同的明文字符串复制一遍得到的密文并不是重复两个密文分组,并且调换密文分组的顺序无法操纵明文。 不要在代码中写死一个固定的密钥和初始化向量,最好和之前提到的盐一样,是唯一、独立并且每次都变化的。推荐使用独立的加密服务来管控密钥、做 加密操作,千万不要把密钥和密文存在一个数据库,加密服务需要设置非常高的管控标准。数据库中不能保存明文的敏感信息,但可以保存脱敏的信息。 普通查询的时候,直接查脱敏信息即可。下面举个例子:

@Data
@Entity
public class UserData {@Idprivate Long id;private String idcard;//脱敏的身份证private Long idcardCipherId;//身份证加密IDprivate String idcardCipherText;//身份证密文private String name;//脱敏的姓名private Long nameCipherId;//姓名加密IDprivate String nameCipherText;//姓名密文
}
@Data
@Entity
public class CipherData {@Id@GeneratedValue(strategy = AUTO)private Long id;private String iv;//初始化向量private String secureKey;//密钥
}

 加密服务使用 GCM 模式( Galois/Counter Mode)的 AES-256 对称加密算法,也就是 AES-256-GCM,这是一种AEAD(Authenticated Encryption with Associated Data)认证加密算法,除了能实现普通加密算法提供的保密性之外,还能实现可认证性和密文完整性,是目前最推荐的 AES 模式。使用类似 GCM 的 AEAD 算法进行加解密,除了需要提供初始化向量和密钥之外,还可以提供一个 AAD(附加认证数据,additional authenticated data),用于验证未 包含在明文中的附加信息,解密时不使用加密时的 AAD 将解密失败。其实,GCM 模式的内部使用的就是 CTR 模式,只不过还使用了 GMAC 签名算法,对 密文进行签名实现完整性校验。

我们实现基于 AES-256-GCM 的加密服务,包含下面的主要逻辑:加密时允许外部传入一个 AAD 用于认证,加密服务每次都会使用新生成的随机值作为密 钥和初始化向量。在加密后,加密服务密钥和初始化向量保存到数据库中,返回加密 ID 作为本次加密的标识。应用解密时,需要提供加密 ID、密文和加 密时的 AAD 来解密。加密服务使用加密 ID,从数据库查询出密钥和初始化向量。


@Service
public class CipherService {//密钥长度public static final int AES_KEY_SIZE = 256;//初始化向量长度public static final int GCM_IV_LENGTH = 12;//GCM身份认证Tag长度public static final int GCM_TAG_LENGTH = 16;@Autowiredprivate CipherRepository cipherRepository;//内部加密方法public static byte[] doEncrypt(byte[] plaintext, SecretKey key, byte[] iv, byte[] aad) throws Exception {//加密算法Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");//Key规范SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");//GCM参数规范GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);//加密模式cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);//设置aadif (aad != null)cipher.updateAAD(aad);//加密byte[] cipherText = cipher.doFinal(plaintext);return cipherText;}//内部解密方法public static String doDecrypt(byte[] cipherText, SecretKey key, byte[] iv, byte[] aad) throws Exception {//加密算法Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");//Key规范SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");//GCM参数规范GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);//解密模式cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);//设置aadif (aad != null)cipher.updateAAD(aad);//解密byte[] decryptedText = cipher.doFinal(cipherText);return new String(decryptedText);}//加密入口public CipherResult encrypt(String data, String aad) throws Exception {//加密结果CipherResult encryptResult = new CipherResult();//密钥生成器KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");//生成密钥keyGenerator.init(AES_KEY_SIZE);SecretKey key = keyGenerator.generateKey();//IV数据byte[] iv = new byte[GCM_IV_LENGTH];//随机生成IVSecureRandom random = new SecureRandom();random.nextBytes(iv);//处理aadbyte[] aaddata = null;if (!StringUtils.isEmpty(aad))aaddata = aad.getBytes();aaddata = aad.getBytes();//获得密文encryptResult.setCipherText(Base64.getEncoder().encodeToString(doEncrypt(data.getBytes(), key, iv, aaddata)));//加密上下文数据CipherData cipherData = new CipherData();//保存IVcipherData.setIv(Base64.getEncoder().encodeToString(iv));//保存密钥cipherData.setSecureKey(Base64.getEncoder().encodeToString(key.getEncoded()));cipherRepository.save(cipherData);//返回本地加密IDencryptResult.setId(cipherData.getId());return encryptResult;}//解密入口public String decrypt(long cipherId, String cipherText, String aad) throws Exception {//使用加密ID找到加密上下文数据CipherData cipherData = cipherRepository.findById(cipherId).orElseThrow(() -> new IllegalArgumentException("invlaid cipherId"));//加载密钥byte[] decodedKey = Base64.getDecoder().decode(cipherData.getSecureKey());//初始化密钥SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");//加载IVbyte[] decodedIv = Base64.getDecoder().decode(cipherData.getIv());//处理aadbyte[] aaddata = null;if (!StringUtils.isEmpty(aad))aaddata = aad.getBytes();//解密return doDecrypt(Base64.getDecoder().decode(cipherText.getBytes()), originalKey, decodedIv, aaddata);}
}

我们可以让用户选择,如果需要保护二要素的话,就自己输入一个查询密码作为 AAD。系统需要读取用户敏感信息的时候,还需要用户提供这个密码,否 则无法解密。这样一来,即使黑客拿到了用户数据库的密文、加密服务的密钥和 IV,也会因为缺少 AAD 无法解密。

    @Autowiredprivate CipherService cipherService;//加密@GetMapping("right")public UserData right(@RequestParam(value = "name", defaultValue = "test") String name,@RequestParam(value = "idcard", defaultValue = "300000000000001234") String idCard,@RequestParam(value = "aad", required = false)String aad) throws Exception {UserData userData = new UserData();userData.setId(1L);//脱敏姓名userData.setName(chineseName(name));//脱敏身份证userData.setIdcard(idCard(idCard));//加密姓名CipherResult cipherResultName = cipherService.encrypt(name,aad);userData.setNameCipherId(cipherResultName.getId());userData.setNameCipherText(cipherResultName.getCipherText());//加密身份证CipherResult cipherResultIdCard = cipherService.encrypt(idCard,aad);userData.setIdcardCipherId(cipherResultIdCard.getId());userData.setIdcardCipherText(cipherResultIdCard.getCipherText());return userRepository.save(userData);}//解密@GetMapping("read")public void read(@RequestParam(value = "aad", required = false)String aad) throws Exception {//查询用户信息UserData userData = userRepository.findById(1L).get();//使用AAD来解密姓名和身份证log.info("name : {} idcard : {}",log.info("name : {} idcard : {}",cipherService.decrypt(userData.getNameCipherId(), userData.getNameCipherText(),aad),cipherService.decrypt(userData.getIdcardCipherId(), userData.getIdcardCipherText(),aad));}//脱敏身份证private static String idCard(String idCard) {String num = StringUtils.right(idCard, 4);return StringUtils.leftPad(num, StringUtils.length(idCard), "*");}//脱敏姓名public static String chineseName(String chineseName) {String name = StringUtils.left(chineseName, 1);return StringUtils.rightPad(name, StringUtils.length(chineseName), "*");

{"id":1,"name":"朱*","idcard":"************** 1234","idcardCipherId":26346,"idcardCipherText":"t/wIh1XTj00wJP1Lt3aGzSvn9GcqQWEwthN58KKU4KZ4Tw==","nameCipherId":26347,"name CipherText":"+gHrk1 mWmveBMVUo+CYon8Zjj9QAtw=="} [21:46:00.079] [http-nio-45678-exec-6] [INFO ] [o.g.t.c.s.s.StoreIdCardController:102 ] - name : test idcard : 300000000000001234

错误的aad会抛出异常 javax.crypto.AEADBadTagException: Tag mismatch! at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:578) at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116) at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853) at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) at javax.crypto.Cipher.doFinal(Cipher.java:2164)

非对称加密

公钥密码算法。公钥密码是由一对密钥对构成的,使用公钥或者说加密密钥来加密,使用私钥或者说解密密钥来解密,公钥可以任意公开,私钥不能公 开。使用非对称加密的话,通信双方可以仅分享公钥用于加密,加密后的数据没有私钥无法解密,国密算法包括SM2,SM9。

相关文章:

项目安全-----加密算法实现

目录 对称加密算法 AES (ECB模式) AES(CBC 模式)。 非对称加密 对称加密算法 对称加密算法,是使用相同的密钥进行加密和解密。使用对称加密算法来加密双方的通信的话,双方需要先约定一个密钥,加密方才能加密&#…...

只用一台服务器部署上线(宝塔面板) 前后端+数据库

所需材料 工具:安装宝塔面板服务器至少一台、域名一个 前端:生成dist文件(前端运行build命令) 后端:生成jar包(maven运行package命令) 准备: 打开宝塔面板,点击进入软…...

《Pandas 简易速速上手小册》第8章:Pandas 高级数据分析技巧(2024 最新版)

文章目录 8.1 使用 apply 和 map 函数8.1.1 基础知识8.1.2 重点案例:客户数据清洗和转换8.1.3 拓展案例一:产品评分调整8.1.4 拓展案例二:地址格式化 8.2 性能优化技巧8.2.1 基础知识8.2.2 重点案例:大型销售数据分析8.2.3 拓展案…...

计算机网络_1.6.2 计算机网络体系结构分层的必要性

1.6.2 计算机网络体系结构分层的必要性 一、五层原理体系结构每层各自主要解决什么问题1、物理层2、数据链路层3、网络层4、运输层5、应用层 二、总结三、练习 笔记来源: B站 《深入浅出计算机网络》课程 本节主要介绍实现计算机网络需要解决哪些问题?以…...

跟着cherno手搓游戏引擎【18】抽象Shader、项目小修改

抽象&#xff1a; Shader.h: #pragma once #include <string>namespace YOTO {class Shader {public:virtual~Shader()default;virtual void Bind()const0;virtual void UnBind()const0;static Shader* Create(const std::string& vertexSrc, const std::string&am…...

每日OJ题_算法_模拟②_力扣495. 提莫攻击

目录 力扣495. 提莫攻击 解析代码 力扣495. 提莫攻击 495. 提莫攻击 难度 简单 在《英雄联盟》的世界中&#xff0c;有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希&#xff08;编者注&#xff1a;寒冰射手&#xff09;进入中毒状态。 当提莫攻击艾希&#xff0c…...

freertos 源码分析二 list链表源码

list.c 一、链表初始化 void vListInitialise( List_t * const pxList ) { pxList->pxIndex ( ListItem_t * ) &…...

Peter算法小课堂—Dijkstra最短路算法

大家好&#xff0c;我们人见人爱、花见花开、车见车爆胎的Peter Pan来啦&#xff0c;hia~hia~hia。今天&#xff0c;我们今天来学习毒瘤的最短路算法啦。啊这……什么是Dijkstra算法&#xff1f;长文警告⚠ 正经点啊 手算样例 大家思考一下&#xff0c;你在手算样例的时候&am…...

Python 读取和写入包含中文的csv、xlsx、json文件

背景 最近在做数据的训练&#xff0c;经常需要读取写入csv、xlsx、json文件来获取数据&#xff0c;在这里做简单总结记录。 ps: 读取和写入中文文件时&#xff0c;需要确保文件的编码格式是正确的。通常情况使用UTF-8编码格式。如果使用其他编码格式可能会导致读取或写入时出…...

【算法】利用递归dfs解决二叉树算法题(C++)

文章目录 1. 前言2. 算法题2331.计算布尔二叉树的值129.求根节点到叶节点数字之和LCR047.二叉树剪枝98.验证二叉搜索树230.二叉搜索树中第K小的元素257.二叉树的所有路径 1. 前言 有关 递归 的相关解释与解题 请看下文&#xff1a; 以汉诺塔理解递归、并用递归解决算法题 对于…...

计算机网络_1.6.1 常见的三种计算机网络体系结构

1.6.1 常见的三种计算机网络体系结构 1、OSI&#xff08;七层协议&#xff09;标准失败的原因2、TCP/IP参考模型3、三种网络体系结构对比 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 1、OSI&#xff08;七层协议&#xff09;标准失败的原因 &#xff08;1&#xf…...

XML传参方式

export function groupLoginAPI(xmlData) {return http.post(/tis/group/1.0/login, xmlData, {headers: {Content-Type: application/xml,X-Requested-With: AAServer/4.0,}}) }import {groupLoginAPI} from "../api/user"; function (e) { //xml格式传参let groupX…...

Pyecharts炫酷散点图构建指南【第50篇—python:炫酷散点图】

文章目录 Pyecharts炫酷散点图构建指南引言安装Pyecharts基础散点图自定义散点图样式渐变散点图动态散点图高级标注散点图多系列散点图3D散点图时间轴散点图笛卡尔坐标系下的极坐标系散点图 总结&#xff1a; Pyecharts炫酷散点图构建指南 引言 在数据可视化领域&#xff0c;…...

关于爬取所有哔哩哔哩、任意图片、所有音乐、的python脚本语言-Edge浏览器插件 全是干货!

这些都是现成的并且实时更新的&#xff01;从次解放双手&#xff01; 首先有自己的edge浏览器基本上都有并且找到插件选项 1.哔哩哔哩视频下载助手&#xff08;爬取哔哩哔哩视频&#xff09; bilibili哔哩哔哩视频下载助手 - Microsoft Edge Addons 下面是效果&#xff1a; 2.图…...

压力测试工具-Jmeter使用总结

目录 一.前言 二.线程组 三.线程组的组件 四.线程组-HTTP请求 1、JSON提取器 2、XPATH提取器 3、正则表达式提取器 五.线程组-断言 1、响应断言 2、JSON断言 六.创建测试 1.创建线程组 2.配置元件 3.构造HTTP请求 4.添加HTTP请求头 5.添加断言 6.添加查看结果树…...

[cmake]CMake Error: Could not create named generator Visual Studio 16 2019解决方法

配置flycv时&#xff0c;cmake以下代码会报错第二行的错误&#xff0c;网上解决方法为第三行代码 cmake .. -G "Visual Studio 16 2019 Win64" CMake Error: Could not create named generator Visual Studio 16 2019 cmake .. -G "Visual Studio 16 2019"…...

2024美赛数学建模D题思路分析 - 大湖区水资源问题

1 赛题 问题D&#xff1a;大湖区水资源问题 背景 美国和加拿大的五大湖是世界上最大的淡水湖群。这五个湖泊和连接的水道构成了一个巨大的流域&#xff0c;其中包含了这两个国家的许多大城市地区&#xff0c;气候和局部天气条件不同。 这些湖泊的水被用于许多用途&#xff0…...

2024 高级前端面试题之 HTTP模块 「精选篇」

该内容主要整理关于 HTTP模块 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTTP模块精选篇 1. HTTP 报文的组成部分2. 常见状态码3. 从输入URL到呈现页面过程3.1 简洁3.2 详细 4. TCP、UDP相关5. HTTP2相关6. https相关7. WebSocket的…...

【Linux C | 网络编程】netstat 命令图文详解 | 查看网络连接、查看路由表、查看统计数据

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

Python爬虫存储库安装

如果你还没有安装好MySQL、MongoDB、Redis 数据库&#xff0c;请参考这篇文章进行安装&#xff1a; Windows、Linux、Mac数据库的安装&#xff08;mysql、MongoDB、Redis&#xff09;-CSDN博客 存储库的安装 上节中&#xff0c;我们介绍了几个数据库的安装方式&#xff0c;但…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...