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.pswp.cn/news/249456.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249456.shtml
英文地址,請注明出處:http://en.pswp.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