DESUtils 加解密时 Given final block not properly padded bug小记

事情的经过是这个样子的。。。。。。
先说说问题是怎么出现的。根据客户需求,需要完成一个一键登录的功能,于是我的项目中就诞生了DesUtil,但是经过上百次用户测试,发现有一个用户登录就一直报错!难道又遇到神坑啦!!发火
让我们先看看源代码,干货来了!

package com.kwp.main.util.security;import java.io.IOException;
import java.security.SecureRandom;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;public class DesUtil {private final static String DES = "DES";public final static String KEY = "EA22DAB57022E2560A376749E3408196A9E287D800E068E5";/*** Description 根据键值进行加密* @param data* @param key  加密键byte数组* @return* @throws Exception*/public static String encrypt(String data, String key) throws Exception {byte[] bt = encrypt(data.getBytes(), key.getBytes());String strs = new BASE64Encoder().encode(bt);return strs;}/*** Description 根据键值进行解密* @param data* @param key  加密键byte数组* @return* @throws IOException* @throws Exception*/public static String decrypt(String data, String key) throws IOException,Exception {if (data == null)return null;BASE64Decoder decoder = new BASE64Decoder();byte[] buf = decoder.decodeBuffer(data);byte[] bt = decrypt(buf,key.getBytes());return new String(bt);}/*** Description 根据键值进行加密* @param data* @param key  加密键byte数组* @return* @throws Exception*/private static byte[] encrypt(byte[] data, byte[] key) throws Exception {// 生成一个可信任的随机数源SecureRandom sr = new SecureRandom();// 从原始密钥数据创建DESKeySpec对象DESKeySpec dks = new DESKeySpec(key);// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);SecretKey securekey = keyFactory.generateSecret(dks);// Cipher对象实际完成加密操作Cipher cipher = Cipher.getInstance(DES);// 用密钥初始化Cipher对象cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);return cipher.doFinal(data);} /*** Description 根据键值进行解密* @param data* @param key  加密键byte数组* @return* @throws Exception*/private static byte[] decrypt(byte[] data, byte[] key) throws Exception {// 生成一个可信任的随机数源SecureRandom sr = new SecureRandom();// 从原始密钥数据创建DESKeySpec对象DESKeySpec dks = new DESKeySpec(key);// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);SecretKey securekey = keyFactory.generateSecret(dks);// Cipher对象实际完成解密操作Cipher cipher = Cipher.getInstance(DES);// 用密钥初始化Cipher对象cipher.init(Cipher.DECRYPT_MODE, securekey, sr);return cipher.doFinal(data);}public static void main(String[] args)throws Exception{//加密System.out.println(DesUtil.encrypt("liujianyong", DesUtil.KEY));//解密System.out.println(DesUtil.decrypt("/s=", DesUtil.KEY));}
}

就是这个人,main方法加解密是不会报错, 而嵌套到URL中就报错了,细心的我最后发现别人转码后有一个“/”, 而这个人转了之后却又两个“/”,于是我想到了URLEncoder,加上之后,确实不报错了,而是后台出来了个大大的bug, 就是这个“Given final block not properly padded”,所以不得不去网上寻找解药。

以下是网上解决方案,非本人原创,特此声明!


仔细分析一下,不难发现,该异常是在解密的时候抛出的,加密的方法没有问题。

但是两个方法的唯一差别是Cipher对象的模式不一样,这就排除了程序写错的可能性。再看一下异常的揭示信息,大概的意思是:提供的字块不符合填补的。什么意思???原来在用DES加密的时候,最后一位长度不足64的,它会自动填补到64,那么在我们进行字节数组到字串的转化过程中,可以把它填补的不可见字符改变了,所以引发系统抛出异常。问题找到,怎么解决呢?大家还记得邮件传输通常会把一些信息编码保存,对了,就是Base64,那样保证了信息的完整性,所以我们就是利用一下下了。为了方便使用,我们再写一个新的方法封装一下原来的方法:

public static String DataEncrypt(String str,byte[] key){

String encrypt = null;

try{

byte[] ret = encode(str.getBytes(“UTF-8”),key);

encrypt = new String(Base64.encode(ret));

}catch(Exception e){

System.out.print(e);

encrypt = str;

}

return encrypt;

}

public static String DataDecrypt(String str,byte[] key){

String decrypt = null;

try{

byte[] ret = decode(Base64.decode(str),key);

decrypt = new String(ret,”UTF-8”);

}catch(Exception e){

System.out.print(e);

decrypt = str;

}

return decrypt;

}

我们把方法的参数改成了字串,但是为什么要用UTF-8呢?不指定它的字节格式不行吗?大家知道,UTF-8是国际通用的字符编码,用它传输任何字串都不会有问题,通过它也可以很完美的解决J2EE的中文问题!所以我们最好用UTF-8编码,以减少不必要的麻烦。


结合上述方法,我的DesUtil修改版诞生了!

package com.kwp.main.util.security;import java.io.IOException;
import java.security.SecureRandom;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;import com.sun.org.apache.xml.internal.security.utils.Base64;public class DesUtil {private final static String DES = "DES";public final static String KEY = "EA22DAB57022E2560A376749E3408196A9E287D800E068E5";/*** Description 根据键值进行加密* @param data* @param key  加密键byte数组* @return* @throws Exception*/private static byte[] encrypt(byte[] data, byte[] key) throws Exception {// 生成一个可信任的随机数源SecureRandom sr = new SecureRandom();// 从原始密钥数据创建DESKeySpec对象DESKeySpec dks = new DESKeySpec(key);// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);SecretKey securekey = keyFactory.generateSecret(dks);// Cipher对象实际完成加密操作Cipher cipher = Cipher.getInstance(DES);// 用密钥初始化Cipher对象cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);return cipher.doFinal(data);}/*** Description 根据键值进行解密* @param data* @param key  加密键byte数组* @return* @throws Exception*/private static byte[] decrypt(byte[] data, byte[] key) throws Exception {// 生成一个可信任的随机数源SecureRandom sr = new SecureRandom();// 从原始密钥数据创建DESKeySpec对象DESKeySpec dks = new DESKeySpec(key);// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);SecretKey securekey = keyFactory.generateSecret(dks);// Cipher对象实际完成解密操作Cipher cipher = Cipher.getInstance(DES);// 用密钥初始化Cipher对象cipher.init(Cipher.DECRYPT_MODE, securekey, sr);return cipher.doFinal(data);}/*** Description 根据键值进行加密* @param data * @param key  加密键byte数组* @return* @throws Exception*/public static String encrypt(String data, String key) throws Exception {if (data == null) return null;String strs = null;  try{  byte[] ret = encrypt(data.getBytes("UTF-8"), key.getBytes("UTF-8"));  strs = new String(Base64.encode(ret));  }catch(Exception e){  System.out.print(e);  strs = data;  }  System.out .println("加密后:" + strs);return strs;}/*** Description 根据键值进行解密* @param data* @param key  加密键byte数组* @return* @throws IOException* @throws Exception*/public static String decrypt(String data, String key) throws IOException, Exception {if (data == null) return null;String strs = null;try{  byte[] ret = decrypt(Base64.decode(data), key.getBytes("UTF-8"));  strs = new String(ret,"UTF-8");  }catch(Exception e){  System.out.print(e);  strs = data;  }  System.out.println("解密后:" + strs);return strs;}public static void main(String[] args)throws Exception{//加密
//      System.out.println(DesUtil.encrypt("liujianyong", "elink!@#$%"));String name = java.net.URLEncoder.encode(DesUtil.encrypt("liujianyong", "elink!@#$%"), "UTF-8");System.out.println(name);System.out.println(java.net.URLDecoder.decode(name, "UTF-8"));System.out.println(DesUtil.decrypt(java.net.URLDecoder.decode(name, "UTF-8"), "elink!@#$%"));//      System.out.println(DesUtil.encrypt("0", DesUtil.KEY));//解密
//      System.out.println(DesUtil.decrypt("/sVcz2jGgPQ=", DesUtil.KEY));}
}

这样修改之后,当在使用这个钉子户登录时不报错,却说用户不存在,第二次点击却又没有问题了,然后就再没有报错。。。 大白天真时见鬼了,就算这个问题解决了吧,希望有缘人等遇到我说的那个错,并干掉他!小弟不胜感激。。。

后记,看了网上的很多例子,DES的加解密还是存在漏洞的,并且安全系数也不高,所以在这里就不推荐大家使用了,如果迫不得已的用了,还点背的碰到这样的错误,这个思路倒是可以借鉴一下! 大神勿喷。。。

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

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

相关文章

Apache

https://www.iteye.com/blog/yaodaqing-1596570

仿 腾讯新闻快讯 --无缝滚动

//无缝滚动function AutoScroll(obj) {var autoScrollTimernull,timernull;timersetTimeout(function(){move();},3000);function move(){clearTime(autoScrollTimer);var liLen $(obj).find(li).length;if(liLen 1){//此处处理只有一条数据时 跳动效果$(obj).find("ul:f…

spring3.2 @Scheduled注解 定时任务

1.首先加入 下载spring3.2 ,http://projects.spring.io/spring-framework/ 2.加入jar包,在applicationContext.xml加入声明-xmlns加入[java xmlns:task"http://www.springframework.org/schema/task" -xsi加入[java] http://www.springframe…

搜索(题目)

A.POJ_1321考查DFS的一个循环中递归调用 1 #include<iostream>2 #include<cstring>3 4 using namespace std;5 char a[10][10]; //记录棋盘位置6 int book[10]; //记录一列是否已经放过棋子7 int n, k; // k 为 需要放入的棋子数8 int t…

rest_framework中的url注册器,分页器,响应器

url注册器&#xff1a; 对于authors表&#xff0c;有两个url显得麻烦&#xff1a; rest_framework将我们的url进行了处理&#xff1a; 这样写了之后&#xff0c;就可以像原来一样访问author表了。 故意写错路径&#xff0c;看看它为我们做了哪些配置&#xff1a; 在有关author的…

Alluxio学习

介绍 Alluxio&#xff08;之前名为Tachyon&#xff09;是世界上第一个以内存为中心的虚拟的分布式存储系统。它统一了数据访问的方式&#xff0c;为上层计算框架和底层存储系统构建了桥梁。应用只需要连接Alluxio即可访问存储在底层任意存储系统中的数据。此外&#xff0c;Allu…

freemarker常见语法大全

FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format} ${book.name?if_exists } //用于判断如果存在,就输出这个值 ${book.name?default(‘xxx’)}//默认值xxx ${book.name!"xxx"}//默认值xxx ${book.date?string(yyy…

网页排版与布局

一 网站的层次结构 制作便于浏览页面的一个大敌就是视觉干扰,它包含两类: a,混乱页面主次不清,所有东西都引人注目 b,背景干扰 1.把页面分割成清晰明确的不同区域很重要,因为可以使用户迅速判断出哪些区域应重点看,哪些可以放心地忽略. 2.创建清晰直观的页面层次结构;越重要越要…

Bash的循环结构(for和while)

在bash有三中类型的循环结构表达方法&#xff1a;for&#xff0c;while&#xff0c;until。这里介绍常用的两种&#xff1a;for和while。 for bash的for循环表达式和python的for循环表达式风格很像&#xff1a; for var in $(ls) doecho "$var"done 取值列表有很多种…

MVVM模式下实现拖拽

MVVM模式下实现拖拽 原文:MVVM模式下实现拖拽在文章开始之前先看一看效果图 我们可以拖拽一个"游戏"给ListBox,并且ListBox也能接受拖拽过来的数据&#xff0c; 但是我们不能拖拽一个"游戏类型"给它。 所以当拖拽开始发生的时候我们必须添加一些限制条件&a…

nodejs变量

https://www.cnblogs.com/vipyoumay/p/5597992.html

jenkins+Docker持续化部署(笔记)

参考资料&#xff1a;https://www.cnblogs.com/leolztang/p/6934694.html &#xff08;Jenkins&#xff08;Docker容器内&#xff09;使用宿主机的docker命令&#xff09; https://container-solutions.com/running-docker-in-jenkins-in-docker/ &#xff08;Running Docker i…

正则表达式之括号

正则表达式&#xff08;三&#xff09; 括号 分组 量词可以作用字符或者字符组后面作为限定出现次数&#xff0c;如果是限制多个字符出现次数或者限制一个表达式出现次数&#xff0c;需要使用括号()将多个字符或者表达式括起来&#xff0c;这样便称为分组。例如(ab)表示“ab”字…

免安装Mysql在Mac中的神坑之Access denied for user 'root'@'localhost' (using password: YES)

眼看马上夜深人静了&#xff0c;研究了一天的问题也尘埃落定了。 废话不多说 直接来干货&#xff01; 大家都知道免安装版本的Mysql, 在Mac中安装完成&#xff08;如何安装详见Mac OS X 下 TAR.GZ 方式安装 MySQL&#xff09;之后&#xff0c;在登录时会遇到没有访问权限的问题…

nodejs函数

https://www.cnblogs.com/yourstars/p/6121262.html

[HNOI2009]梦幻布丁

题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,…

用jquery实现html5的placeholder功能

版权声明&#xff1a;本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/QianShouYuZhiBo/article/details/28913501 html5的placeholder功能在表单中经经常使用到。它主要用来提示用户输入信息&#xff0c;当用户点击该输入框之后&#xff0c;提示文字会自己…

mac环境下node.js和phonegap/cordova创建ios和android应用

mac环境下node.js和phonegap/cordova创建ios和android应用 一介布衣 2015-01-12 nodejs 6888 分享到&#xff1a;QQ空间新浪微博腾讯微博人人网微信引用百度百科的一段描述:PhoneGap是一个用基于HTML&#xff0c;CSS和JavaScript的&#xff0c;创建移动跨平台移动应用程序的…

java中多线程 - 多线程中的基本方法

介绍一下线程中基本的方法使用 线程睡眠sleep() Thread.sleep(毫秒);我们可以通过sleep方法设置让线程睡眠。可以看到sleep是个静态方法 public static native void sleep(long var0) throws InterruptedException; try {System.out.println(new Date().getSeconds());Thread.s…

nodejs匿名函数

https://www.cnblogs.com/sharpest/p/8056232.html