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

2024-08-31 20:18

本文主要是介绍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的加解密还是存在漏洞的,并且安全系数也不高,所以在这里就不推荐大家使用了,如果迫不得已的用了,还点背的碰到这样的错误,这个思路倒是可以借鉴一下! 大神勿喷。。。

这篇关于DESUtils 加解密时 Given final block not properly padded bug小记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

SpringBoot实现接口数据加解密的三种实战方案

《SpringBoot实现接口数据加解密的三种实战方案》在金融支付、用户隐私信息传输等场景中,接口数据若以明文传输,极易被中间人攻击窃取,SpringBoot提供了多种优雅的加解密实现方案,本文将从原... 目录一、为什么需要接口数据加解密?二、核心加解密算法选择1. 对称加密(AES)2. 非对称加密(R

Java实现MinIO文件上传的加解密操作

《Java实现MinIO文件上传的加解密操作》在云存储场景中,数据安全是核心需求之一,MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,下面我们来看看如何通过Java... 目录一、背景与需求二、技术选型与原理1. 加密方案对比2. 核心算法选择三、完整代码实现1. 加密上

springboot实现配置文件关键信息加解密

《springboot实现配置文件关键信息加解密》在项目配置文件中常常会配置如数据库连接信息,redis连接信息等,连接密码明文配置在配置文件中会很不安全,所以本文就来聊聊如何使用springboot... 目录前言方案实践1、第一种方案2、第二种方案前言在项目配置文件中常常会配置如数据库连接信息、Red

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

ZOJ Monthly, August 2014小记

最近太忙太忙,只能抽时间写几道简单题。不过我倒是明白要想水平提高不看题解是最好的了。 A  我只能死找规律了,无法证明 int a[50002][2] ;vector< vector<int> > gmax , gmin ;int main(){int n , i , j , k , cmax , cmin ;while(cin>>n){/* g

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

2014 Multi-University Training Contest 8小记

1002 计算几何 最大的速度才可能拥有无限的面积。 最大的速度的点 求凸包, 凸包上的点( 注意不是端点 ) 才拥有无限的面积 注意 :  凸包上如果有重点则不满足。 另外最大的速度为0也不行的。 int cmp(double x){if(fabs(x) < 1e-8) return 0 ;if(x > 0) return 1 ;return -1 ;}struct poin

2014 Multi-University Training Contest 7小记

1003   数学 , 先暴力再解方程。 在b进制下是个2 , 3 位数的 大概是10000进制以上 。这部分解方程 2-10000 直接暴力 typedef long long LL ;LL n ;int ok(int b){LL m = n ;int c ;while(m){c = m % b ;if(c == 3 || c == 4 || c == 5 ||

2014 Multi-University Training Contest 6小记

1003  贪心 对于111...10....000 这样的序列,  a 为1的个数,b为0的个数,易得当 x= a / (a + b) 时 f最小。 讲串分成若干段  1..10..0   ,  1..10..0 ,  要满足x非递减 。  对于 xi > xi+1  这样的合并 即可。 const int maxn = 100008 ;struct Node{int