程序猿成长之路之密码学篇-分组密码加密模式及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后续再讲————————————————————

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/85347.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10个AI绘图生成器让绘画更简单

AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中发挥着越来越大的作用&#xff0c;开创了AI绘画生成器新时代。在绘画领域当然也是如此&#xff0c;与传统的绘画工具不同&#xff0c;AI人工智能时代的绘画工具是全自动的、智能的&#xff0c;甚至可以说是“傻瓜式”…

Three.js阴影

目录 Three.js入门 Three.js光源 Three.js阴影 Three.js纹理贴图 使用灯光后&#xff0c;场景中就会产生阴影。物体的背面确实在黑暗中&#xff0c;这称为核心阴影&#xff08;core shadow&#xff09;。我们缺少的是落下的阴影&#xff08;drop shadow&#xff09;&#…

【ultralytics仓库使用自己的数据集训练RT-DETR】

ultralytics仓库使用自己的数据集训练RT-DETR RT-DETR由百度开发&#xff0c;是一款尖端的端到端物体检测器&#xff08;基于transformer架构&#xff09;&#xff0c;在提供实时性能的同时保持高精度。它利用视觉变换器&#xff08;ViT&#xff09;的力量&#xff0c;通过解耦…

水库大坝安全监测系统实施方案

一、方案概述 水库大坝作为特殊的建筑&#xff0c;其安全性质与房屋等建筑物完全不同&#xff0c;并且建造在地质构造复杂、岩土特性不均匀的地基上&#xff0c;目前对于大坝监测多采用人工巡查的方法&#xff0c;存在一定的系统误差&#xff0c;其工作性态和安全状况随时都在变…

SSH连接阿里服务器搭建JAVA环境

SSH连接 1.IP录入 2.账户登录 3.右下角点击连接即可 安装JDK 1.查看是否存在jdk yum search jdk 2.安装JDK&#xff08;不存在时&#xff09; yum -y install java-1.8.0-openjdk* 3.安装完成 4.测试成功否 java -version 安装文件上传 yum -y install lrzsz 上传文件…

[git] git基础知识

git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目 git易于学习&#xff0c;性能极快 什么是版本控制&#xff1f; 版本控制是一种记录文件内容变化&#xff0c;以便将来查阅特定版本修订情况&#xff0c;可以记录文件修改历史…

微信开发之获取收藏夹列表的技术实现

简要描述&#xff1a; 获取收藏夹内容 请求URL&#xff1a; http://域名地址/weChatFavorites/favSync 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数…

将Linux上的cpolar内网穿透配置为开机自启动——“cpolar内网穿透”

将Linux上的cpolar内网穿透配置为开机自启动 文章目录 将Linux上的cpolar内网穿透配置为开机自启动前言一、进入命令行模式二、输入token码三、输入内网穿透命令 前言 我们将cpolar安装到了Ubuntu系统上&#xff0c;并通过web-UI界面对cpolar的功能有了初步了解。当然cpolar除…

黑马头条项目学习--Day3: 自媒体文章发布

Day3: 自媒体文章发布 Day3: 自媒体文章发布1) 素材管理-图片上传a) 前期微服务搭建b) 具体实现 2) 素材管理-图片列表a) 接口定义b) 具体实现 3) 素材管理-照片删除/收藏a) 图片删除a1) 接口定义a2) 代码实现 b) 收藏与取消b1) 接口定义b2) 代码实现 4) 文章管理-频道列表查询…

shell脚本之正则表达式

目录 一.常见的管道命令1.1sort命令1.2uniq命令1.3tr命令1.4cut命令1.5实例1.5.1统计当前主机连接状态1.5.2统计当前主机数 二.正则表达式2.1正则表达式的定义2.2常见元字符&#xff08;支持的工具&#xff1a;find&#xff0c;grep&#xff0c;egrep&#xff0c;sed和awk&…

面试总结-c++

1该吹牛逼吹牛逼。在自己能说出个所以然的情况下&#xff0c;该吹就吹&#xff0c;不吹没工作&#xff0c;吹了有希望。 比如 c组长&#xff0c;确有其事&#xff0c;但是挺唬人。说自己在北京定居也是侧面吹牛逼&#xff0c;证明自己的能力。还有媳妇在研究所。 2.对自己做过…

【论文阅读】EULER:通过可扩展时间链接预测检测网络横向移动(NDSS-2022)

作者&#xff1a;乔治华盛顿大学-Isaiah J. King、H. Howie Huang 引用&#xff1a;King I J, Huang H H. Euler: Detecting Network Lateral Movement via Scalable Temporal Graph Link Prediction [C]. Proceedings 2022 Network and Distributed System Security Symposium…

【JavaSpring】注解开发

注解开发定义bean 不指定名称 package org.example.service.impl;import org.example.dao.BookDao; import org.example.service.BookService; import org.springframework.stereotype.Component;Component public class BookServiceimpl implements BookService {private Bo…

QGIS开发五:VS使用QT插件创建UI界面

前面我们说了在创建项目时创建的是一个空项目&#xff0c;即不使用 Qt 提供的综合开发套件 Qt Creator&#xff0c;也不使用 Qt Visual Studio Tools 这类工具。 但是后面发现&#xff0c;如果我想要有更加满意的界面布局&#xff0c;还是要自己写一个UI文件&#xff0c;如果不…

pdf怎么压缩到1m?这样做压缩率高!

PDF是目前使用率比较高的一种文档格式&#xff0c;因为它具有很高的安全性&#xff0c;还易于传输等&#xff0c;但有时候当文件体积过大时&#xff0c;会给我们带来不便&#xff0c;这时候简单的解决方法就是将其压缩变小。 想要将PDF文件压缩到1M&#xff0c;也要根据具体的情…

Docker环境下MySQL备份恢复工具XtraBackup使用详解 | Spring Cloud 62

一、XtraBackup 简介 Percona XtraBackup是一个开源的MySQL和MariaDB数据库备份工具&#xff0c;它能够创建高性能、一致性的备份&#xff0c;并且对生产环境的影响很小。Percona XtraBackup通过在不停止MySQL服务器的情况下&#xff0c;复制InnoDB存储引擎的数据文件和事务日…

css实现,正常情况下div从左到右一次排列,宽度超出时,右侧最后一个div固定住,左侧其他div滚动

需求:正常情况下 宽度超出时: 实现: <templete><div class"jieduanbox"><div v-for"(item, index) in stageList" :key"index" style"display: inline-block">.......</div><div class"rightBtn&q…

面向云思考安全

Gartner最近的一项研究表明&#xff0c;到 2025 年&#xff0c;85% 的企业会采用云战略&#xff0c;虽然这一数字是面向全球的&#xff0c;但可以看到在中国的环境中&#xff0c;基于云所带来的优势&#xff0c;越来越多的企业也同样开始积极向云转型。 但同时&#xff0c;有报…

39 printf 的输出到设备层的调试

前言 在前面 printf 的调试 我们只是调试到了 glibc 调用系统调用, 封装了参数 stdout, 带输出的字符缓冲, 以及待输出字符长度 然后内核这边 只是到了 write 的系统调用, 并未向下细看 我们这里 稍微向下 细追一下, 看看 到达设备层面 这里是怎么具体的 impl 的 测试用例…

Android Studio 屏幕适配

Android开发屏幕适配流程 首先studio中没有ScreenMatch这个插件的&#xff0c;下去现在这个插件 点击File->settings->Plugins->(搜索ScreenMatch插件)&#xff0c;点击下载&#xff0c;应用重启Studio即可&#xff0c;如下图 在values下 创建dimens.xml&#xff0c…