程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解
Cipher.getInstance("AES/ECB/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
在进行加解密编程的时候应该有很多小伙伴接触过以上的语句,但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding的含义?如果不清楚的话那么希望这篇文章可以帮到你们。
什么是分组密码?
分组密码就是按照固定长度的字符对明文加密/密文解密的一种加解密算法。常见的有DES、AES等。具体如下图所示

什么是IV(偏移量)?
可以理解为加解密过程中设置的可变变量,可以是时间戳、uuid也可以是其他随机值,目的是为了增加混乱度,降低被破译的风险。
什么是分组加密模式?
对于一般加解密(或ECB)而言,我们只是调用了加解密函数分组加密就完事了,但是如果遇到了重放攻击(多次发起同一请求),那么黑客在截获密文后可以较为轻松的按照分组进行解密,那么这时候就会对系统安全性造成威胁。为了防止这种情况的发生,我们就可以采取一定的措施去对分组密码进行轮次迭代处理,上述提到的ECB(电子密码本模式)/CBC(密文分组链接模式)就属于分组加密模式。此外还有CFB(密文反馈模式)等也属于分组加密模式。
下面我们来一一分析不同分组加密模式的含义以及各自的优缺点。
ECB(电子密码本模式)

过程/原理:
这种分组加密模式就属于最简单的不经过处理的分组加密模式,直接对明文或密文按组进行加密或解密得到结果。
优点:
-支持多线程异步处理,效率较高
-无需分组轮次迭代计算,计算量更小
缺点:
-安全性能较差,容易被攻破
CBC(密文分组链接模式)

过程/原理:
加密:
第一轮:生成初始化向量iv,与第一组明文分组异或运算后共同加密。
后续:拿上一轮CBC运算结果与当前分组明文异或运算后加密。
解密:
第一轮:生成初始化向量iv,先对第一组密文分组解密后再与初始化向量进行异或运算。
后续:先对当前密文分组解密后再与上一轮密文分组进行异或运算
加解密原理:
A异或B异或B = A
例如第一轮加密后的第一组密文 e1 = Ek(iv xor 第一组明文)
则第一轮解密后的第一组明文 d1 = (Ek’(第一组密文) xor iv) = (iv xor 第一组明文) xor iv = 第一组明文
优点:
-使用了iv作为随机变量,增加了破译的难度,使得每次针对同一明文加密后的结果不一致
-加密使用前一轮的输出作为后一轮的输入
缺点:
-增加了计算量,加大了计算开销
CFB(密文反馈模式)

过程/原理:
加密:
第一轮:生成初始化向量iv,先进行加密计算后与第一组明文分组进行异或运算。
后续:拿上一轮CFB运算结果先加密后与当前分组明文进行异或运算。
解密:
第一轮:生成初始化向量iv,先进行加密计算后与第一组密文分组进行异或运算。
后续:拿上一轮CFB运算结果先加密后与当前分组密文进行异或运算。
加解密原理:
A异或B异或B = A
例如第一轮加密后的第一组密文 e1 = Ek(iv) xor 第一组明文
则第一轮解密后的第一组明文 d1 = (Ek(iv) xor 第一组密文) = (Ek(iv) xor (Ek(iv) xor 第一组明文) ) = 第一组明文
优点:
-使用了iv作为随机变量,增加了破译的难度,使得每次针对同一明文加密后的结果不一致
-加密使用前一轮的输出作为后一轮的输入
-iv在分组加密算法中可以单独使用
缺点:
-增加了计算量,加大了计算开销
分组加密模式代码实现
注:如需要AES代码详见以下文章:
程序猿成长之路之密码学篇-AES算法解密详解及代码呈现 https://blog.csdn.net/qq_31236027/article/details/131206018
枚举类型
public enum EncryptMode {CBC("CBC"),CFB("CFB");private String name;private EncryptMode(String name) {this.setName(name);}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
加密部分
/*** 分组加密(128位一组)【完整版】* @param text 明文* @param mode 加密模式(CFB、CBC)* @param iv 偏移量*/@Overridepublic String encrypt(String text, String iv, EncryptMode mode) {if(mode == null) {return encrypt(text);}String result = null;switch(mode) {case CBC: result = encryptCBC(text,iv); break;case CFB: result = encryptCFB(text,iv);break;default: result = encrypt(text);break;}return result;}/*** 分组加密(128位一组)(无iv【偏移量】版)* @param text 明文*/private String encrypt(String text) {StringBuilder sb = new StringBuilder();int textLen = text.length();//获取分组长度// DIV_LEN * CHAR_LEN = 128// 根据DIV_LEN进行分组,如CHAR_LEN=16位,那么就每8个字符一组int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);//分组加密处理for (int i = 0; i < divLen; i++) {int startIndex = i * AESConstant.DIV_LEN;int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);String substr = text.substring(startIndex, endIndex);//尾部填充while(substr.length() < AESConstant.DIV_LEN) {substr += " ";}sb.append(EncodeUtil.binaryToHexStr(baseEncrypt(substr)).trim());}return new BASE64Encoder().encode(sb.toString().trim().getBytes());}/*** 分组加密(128位一组),(有iv【偏移量】CBC版,更安全)* * CBC特性* 1. 每一组分组的密文都依赖于上一组的结果* 2. 加入了iv偏移量使得每次加密执行后的结果都不一致* * @param text 明文* @param iv 偏移量*/private String encryptCBC(String text,String iv) {StringBuilder sb = new StringBuilder();int textLen = text.length();//获取分组长度// DIV_LEN * CHAR_LEN = 128// 根据DIV_LEN进行分组,如CHAR_LEN=16位【UNICODE】,那么就每8个字符一组int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);// CFB加密初始化向量String encryptedPart = iv;//分组加密处理for (int i = 0; i < divLen; i++) {int startIndex = i * AESConstant.DIV_LEN;int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);String substr = text.substring(startIndex, endIndex);//尾部填充while(substr.length() < AESConstant.DIV_LEN) {substr += " ";}while(encryptedPart.length() < AESConstant.DIV_LEN) {encryptedPart += " ";}//CBC关键,需要拿明文与上一轮结果进行异或得到的结果共同加密作为下一轮的输入encryptedPart = EncodeUtil.binaryToStr(baseEncrypt(strXor(encryptedPart,substr)), 16);sb.append(encryptedPart);}//批量处理为16进制后base64运算String result = sb.toString().trim();result = EncodeUtil.strtoBinary(result, 16);result = EncodeUtil.binaryToHexStr(result);return new BASE64Encoder().encode(result.getBytes());}/*** 分组加密(128位一组),(有iv【偏移量】CFB版,更安全)* * CFB特性* * @param text 明文* @param iv 偏移量*/private String encryptCFB(String text,String iv) {StringBuilder sb = new StringBuilder();int textLen = text.length();//获取分组长度// DIV_LEN * CHAR_LEN = 128// 根据DIV_LEN进行分组,如CHAR_LEN=16位【UNICODE】,那么就每8个字符一组int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);// CFB加密初始化向量String encryptedPart = iv;//分组加密处理for (int i = 0; i < divLen; i++) {int startIndex = i * AESConstant.DIV_LEN;int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);String substr = text.substring(startIndex, endIndex);//尾部填充while(substr.length() < AESConstant.DIV_LEN) {substr += " ";}while(encryptedPart.length() < AESConstant.DIV_LEN) {encryptedPart += " ";}//CFB关键,需要拿明文与上一轮加密结果进行异或得到的结果作为下一轮的输入encryptedPart = strXor(EncodeUtil.binaryToStr(baseEncrypt(encryptedPart), 16),substr);sb.append(encryptedPart);}//批量处理为16进制后base64运算String result = sb.toString().trim();result = EncodeUtil.strtoBinary(result, 16);result = EncodeUtil.binaryToHexStr(result);return new BASE64Encoder().encode(result.getBytes());}
解密部分
/*** 分组解密(128位一组)【完整版】* @param encrytedText 密文* @param mode 加密模式(CFB、CBC)* @param iv 偏移量*/@Overridepublic String decrypt(String encrytedText, String iv, EncryptMode mode) {if(mode == null) {return decrypt(encrytedText);}String result = null;switch(mode) {case CBC: result = decryptCBC(encrytedText,iv); break;case CFB: result = decryptCFB(encrytedText,iv);break;default: result = decrypt(encrytedText);break;}return result;}/*** 分组解密* @param encrytedText 密文*/private String decrypt(String encrytedText) {try {//base64解码byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);String str = new String(bytes,Charset.forName("UTF8"));int textLen = str.length();StringBuilder sb = new StringBuilder();int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)//分组解密for (int i = 0; i< divLen; i++) {int startIndex = i * (4*8);int endIndex = (startIndex + (4*8));String temp = str.substring(startIndex, endIndex);sb.append(baseDecrypt(temp));}return sb.toString();} catch (IOException e) {e.printStackTrace();}return null;}/*** 分组解密(128位一组),(有iv【偏移量】CBC版)* @param encrytedText 密文* @param iv 偏移量* @return 明文*/private String decryptCBC(String encrytedText,String iv) {try {//base64解码byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);String str = new String(bytes,Charset.forName("UTF8"));int textLen = str.length();StringBuilder sb = new StringBuilder();int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)//CFB解密初始化向量String decryptedPart = iv;//分组解密for (int i = 0; i< divLen; i++) {int startIndex = i * (4*8);int endIndex = (startIndex + (4*8));String temp = str.substring(startIndex, endIndex);//尾部填充while(decryptedPart.length() < AESConstant.DIV_LEN) {decryptedPart += " ";}//转换成16位的字符,方便strXor运算sb.append(strXor(baseDecrypt(temp),decryptedPart));//位数转换decryptedPart = EncodeUtil.binaryToStr(EncodeUtil.toBinary(temp, EncodeRadix.HEX), 16);}return sb.toString();} catch (IOException e) {e.printStackTrace();}return null;}/*** 分组解密(128位一组),(有iv【偏移量】CFB版)* @param encrytedText 密文* @param iv 偏移量* @return 明文*/private String decryptCFB(String encrytedText,String iv) {try {//base64解码byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);String str = new String(bytes,Charset.forName("UTF8"));int textLen = str.length();StringBuilder sb = new StringBuilder();int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)//CFB解密初始化向量(转为16进制方便计算String decryptedPart = iv;//分组解密for (int i = 0; i< divLen; i++) {int startIndex = i * (4*8);int endIndex = (startIndex + (4*8));String temp = str.substring(startIndex, endIndex);//转换成16位的字符,方便strXor运算temp = EncodeUtil.binaryToStr(EncodeUtil.toBinary(temp, EncodeRadix.HEX), 16);//尾部填充while(decryptedPart.length() < AESConstant.DIV_LEN) {decryptedPart += " ";}//转换成16位的字符,方便strXor运算sb.append(strXor(EncodeUtil.binaryToStr(baseEncrypt(decryptedPart), 16),temp));decryptedPart = temp;}return sb.toString();} catch (IOException e) {e.printStackTrace();}return null;}
运行代码
public static void main(String[] args) {AesUtil util = new AesUtil();//偏移量(8个字符,每个字符16位)String iv = UUID.randomUUID().toString().substring(0,8);//CFB(密文反馈模式)String encrytedStr = util.encrypt("{\"code\":200,\"message\":\"成功!\",\"data\":{\"id\":\"2103813902831\",\"name\":\"章鱼哥是我啊\",\"gender\":\"男\"}}",iv,EncryptMode.CFB);System.out.println("encrytedStr = " + encrytedStr);System.out.println("result= " + util.decrypt(encrytedStr,iv,EncryptMode.CFB));}
最后是运行截图

————————————————PKCS5Padding后续再讲————————————————————
相关文章:
程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解
Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句,但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…...
Windows下批处理删除文件
最近我使用Maven的时候会出现下载jar包不成功的现象,然后需要把它删除然后重新下载,但是有时候文件过多,一个个删除太花费时间,所以用bat的批处理会很舒服。 bat的语法我之前没遇到过,然后我是边学习边试验࿰…...
html中文件上传储存到本地路径
第一步:写html文件 <form action"/uplode" method"post" enctype"multipart/form-data">姓名:<input type"text" name"username"><br>年龄:<input type"text" name"age"><…...
第九章 SpringBoot 自动配置原理 入门
1. 引导加载自动配置类 SpringBootApplication -- SpringBootConfiguration -- EnableAutoConfiguration -- ComponentScan //SpringBootApplicationSpringBootConfiguration EnableAutoConfiguration ComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, cl…...
String str=new String(“tango“) 创建了几个对象?
面试回答 创建的对象数 应该是1个或者2个。 首先要清楚什么是对象? Java 是一种面向对象的语言,而 Java 对象在 JVM 中的存储也是有一定的结构的,在 HotSpot 虚拟机中,存储的形式就是 oop-klass model,即 Java 对象模型…...
引入三阶失真的非线性放大器的模拟输出及使用中值滤波器去除峰值研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 观察者模式使用三个类Subject、Observer和Client。Subject…...
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取 中文公司名称分词工具,支持公司名称中的地名,品牌名(主词),行业词,公司名后缀提取。 对公司名文本解析,识…...
网络编程(JavaEE初阶系列10)
目录 前言: 1.网络编程的基础 1.1为什么需要网络编程 1.2什么是网络编程 1.3网络编程中的基本概念 1.3.1发送端和接收端 1.3.2请求和响应 1.3.3客户端和服务端 2.Socket套接字 2.1概念 2.2分类 3.UDP数据报套接字编程 3.1DataGramSocket API 3.2Datagr…...
Git常用的指令
Git常用的指令 OMMP提交代码的流程 0、配置: git config --list 查看当前配置 git congig --global user.name user 这个会显示你的提交到git的名字 格式:git config [–local|–global|–system] –unset section.key 格式:git config [–l…...
LoadRunner(2)
一、Controller 1.1场景设计 1.通过VUG打开 施压机器:发起请求的角色(用户本地电脑) 被压机器:处理请求的角色(服务器) 2.直接双击Controller 场景设计:需要关注三个部分 第一部分: 第二部分: 2.1运行场景…...
CTF之逆向之阿里巴巴
题目地址:http://www.shiyanbar.com/ctf/13 题目预览: 解题过程: 1、下载附件发现是exe文件 2、使用PEid和Detect It Easy查壳 和 开发语言,发现没有加壳,都是用C#开发的 3、C#和Java Python属于解释型语言ÿ…...
Labview控制APx(Audio Precision)进行测试测量(五)
驱动程序 VIs如何处理配置设置中的单元 APx500 应用程序具有复杂的控件,具有以下功能: 数值和单位组合在一个控制中(例如,1.000 Vrms ) •值转换为 SI 格式(例如,1.000 mVrms 或 1.000 μVrms) •单位之间的转换发生在控制(例如,V…...
在单元测试中使用Jest模拟VS Code extension API
对VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS C…...
django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比
一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…...
谈谈闭包和闭包使用场景
一、什么是闭包 概念:闭包还是作用域的一种特殊应用 二、触发闭包的情况 1.函数当做返回值被返回 2.函数当做参数被传递 3.自执行匿名函数 //情况1:函数当做返回值被返回 function fn(){const a 1;return function(){console.log(a) //1}; } const a …...
MATLAB算法实战应用案例精讲-【图像处理】边界框锚框
目录 目标检测 应用场景 目标检测发展历程 常用数据集 边界框(bounding box)...
04什么场景要用到微服务
一句话导读 根据微服务的特点,可以总结为在构建复杂的、大型的、分布式的、高可用、高并发、高性能的应用时可以使用微服务架构。 目录 一句话导读 一、微服务适用场景 1.业务复杂,模块多且相对独立 2.团队多,管理隔离 3.应用规模大&#…...
.NET SqlSuger 简单介绍,超快开发数据库
文章目录 前言SqlSugar使用我的环境Nuget 安装新建连接串DB First 和 Code First使用增删改查 总结 前言 我之前介绍过EFCore 怎么使用Nuget快速创建数据库,我之后发现SqlSugar更快。这里简单再说一下SqlSugar如何使用 .NET Core 数据库DB First自动生成࿰…...
SpringBoot复习:(28)【前后端不分离】自定义View
一、自定义View package cn.edu.tju.view;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Comp…...
从实验室到菜园子:用SOD、POD、CAT指标,在家也能判断植物是否“亚健康”
从实验室到菜园子:用SOD、POD、CAT指标,在家也能判断植物是否“亚健康” 周末给阳台的番茄浇水时,发现底部叶片边缘开始泛黄。这既不是常见的红蜘蛛危害,也不像缺肥症状——作为都市种植爱好者,我们常会遇到这种"…...
Algorithm-Implementations 实战应用:解决经典编程问题的10个案例
Algorithm-Implementations 实战应用:解决经典编程问题的10个案例 【免费下载链接】Algorithm-Implementations Share, discuss and learn about algorithm implementations! 项目地址: https://gitcode.com/gh_mirrors/al/Algorithm-Implementations Algori…...
微信聊天记录永久保存完整指南:三步轻松备份你的数字记忆
微信聊天记录永久保存完整指南:三步轻松备份你的数字记忆 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/We…...
SciPy优化算法实践:从本地搜索到全局优化
1. SciPy优化算法概述在科学计算和工程应用中,函数优化是一个基础而重要的问题。简单来说,优化就是寻找使目标函数取得最小值或最大值的输入参数。Python的SciPy库为我们提供了一套完整的优化工具集,涵盖了从简单的一维搜索到复杂的多维全局优…...
[具身智能-433]:WebSocket 本质上是一个“披着 HTTP 外衣的 TCP 长连接”的后台通信机制。
WebSocket 是现代 Web 开发中实现实时通信的核心技术。正如我们之前沟通的,它虽然“借用”了 HTTP 来完成握手,但本质上是一个独立的、运行在 TCP 之上的应用层协议(标准文档为 RFC 6455)。 为了让你全面掌握 WebSocketÿ…...
“探索型 AI“和“交付型AI“是两个完全不同的物种 [特殊字符]
"探索型 AI"和"交付型 AI"是两个完全不同的物种 🧬 大家好,我是 AI 研究员阿满 🌸 今天聊一个我自己瞎编的词——不是什么学术界黑话,就是我用了一年 AI 之后的真实体感: "探索型 AI"和…...
如何零基础快速上手专业网络拓扑图绘制?终极免费开源工具指南
如何零基础快速上手专业网络拓扑图绘制?终极免费开源工具指南 【免费下载链接】easy-topo vuesvgelement-ui 快捷画出网络拓扑图 项目地址: https://gitcode.com/gh_mirrors/ea/easy-topo 你是否曾经为绘制复杂的网络拓扑图而头疼?专业工具太复杂…...
Qt表格里放下拉框,选setIndexWidget还是QItemDelegate?一个真实项目踩坑后的选择指南
Qt表格下拉框方案深度对比:从setIndexWidget到QItemDelegate的实战抉择 在开发一个需要动态生成带下拉框表格的报表工具时,我遇到了一个看似简单却暗藏玄机的技术选择——如何在QTableView中实现下拉框功能?经过反复试错和性能测试࿰…...
告别黄牛!3分钟配置Python大麦网抢票神器,演唱会门票轻松到手
告别黄牛!3分钟配置Python大麦网抢票神器,演唱会门票轻松到手 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为抢不到演唱会门票而烦恼吗?每次热门演出开…...
SpringBoot+Vue数码商城系统源码包|含部署教程与设计文档|电商网站开发实战(Java/MySQL/前后端分离)
温馨提示:文末有联系方式项目概览 基于SpringBoot后端与Vue前端构建的现代化数码电商平台,专为电子产品场景深度优化,集成协同过滤智能推荐算法,助力提升用户转化率与复购率。技术架构亮点 采用B/S架构与标准MVC模式,严…...
