報錯的意思的是使用該種解密方式出入長度應為16bit的倍數,但實際的錯誤卻不是這個,錯誤原因根本上是因為在http請求是特殊字符編碼錯誤,具體就是base64生成的+號,服務器接收時成了空格,然后導致base64解碼出的字節數組有改變。
下面來還原並分析一下具體原因:
請求代碼:
@Test public void testHttp() throws Exception { //16進制字節數組,單純的為了生成含有+的base64位字符串,是在試了好多遍都沒試到+,只能把我錯誤例子的byte數組轉成16進制,為我的機智點個贊 String src = "34aa8bc0adaccbe45ac8c7e43d25955f4d3b51a189339af3d5b968b49764f4def4b81ab285a461504a5cb930c08055f96e875e2b4be390f6708ae686b13e8ff3bed9e9455c638c3809e4c51db05ac3f496be28772829270998eddb9ec6c08c4a0cc1d23e59c6ebe8b73b75013ba9eee5"; byte[] bytes = Hex.decodeHex(src); String base64Str = Base64.encodeBase64String(bytes); System.out.println("發送時的字符串:"+base64Str); for(int i=0;i<bytes.length;i++) { System.out.println(bytes[i]); } String httpUrl = "http://10.20.20.184:10086/dbzx/hello?src="+base64Str; HttpRequest.doGet(httpUrl); }
打印結果(部分結果):
發送時的字符串:NKqLwK2sy+RayMfkPSWVX007UaGJM5rz1blotJdk9N70uBqyhaRhUEpcuTDAgFX5bodeK0vjkPZwiuaGsT6P877Z6UVcY4w4CeTFHbBaw/SWvih3KCknCZjt257GwIxKDMHSPlnG6+i3O3UBO6nu5Q== 52 -86 -117 -64 -83 -84 -53 -28 90 -56 -57 -28
服務端代碼:
package com.dbzx.controller; import org.apache.commons.codec.binary.Base64; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.dbzx.common.ResultModel; @RestController @RequestMapping("dbzx") public class HelloController { @RequestMapping("/hello") public ResultModel hello(String src) { System.out.println("接收到的字符串:"+src); byte[] bytes = Base64.decodeBase64(src); for(int i=0;i<bytes.length;i++) { System.out.println(bytes[i]); } return ResultModel.ok(); } }
打印結果(部分結果):
接收到的字符串:NKqLwK2sy RayMfkPSWVX007UaGJM5rz1blotJdk9N70uBqyhaRhUEpcuTDAgFX5bodeK0vjkPZwiuaGsT6P877Z6UVcY4w4CeTFHbBaw/SWvih3KCknCZjt257GwIxKDMHSPlnG6 i3O3UBO6nu5Q== 52 -86 -117 -64 -83 -84 -55 22 -78 49 -7 15
通過兩端的結果對比可以發現發送前是+,服務器接收的是空格,這是因為有些特殊字符在http請求時是無法編碼的,其實base64就是解決類似問題得,只是越來越多的特殊字符,導致它出錯。
有些人說base64會把+和空格轉成相同的字節碼,不會出錯,但是我們來對比一下字節碼。
可以看到,在第7行開始不對照。
而原本字符串(NKqLwK2sy+)+在第10個位置,跟據base64的規則,4個base64轉成的字符串,代表3個原本的字符串,而上圖第七個字節,需要"y+"共同構造,這就是出錯的原因。既是aes解碼出錯的原因。
至於16的倍數,理論上只用是偶數位的字節數都滿足,顯示錯誤出在其他方面,這個例子就是字節碼出錯了。
解決的方法有多種:
一、最直接的解決方式,把字符串中的空格,轉化成+
str = str.replace(" ", "+");
二、換一種傳遞參數的方式,保證沒有特殊字符,只有數字和字母。
在我的項目里是為了保aes加密后的數據傳輸,加密后是byte[],所以我把結果變成16進制的字符串輸出,這樣接收參數沒有改變。
String token = Hex.encodeHexString(resultBytes);//發送方轉成16進制字符串 byte[] tokenBytes = Hex.decodeHex(token);//接收方16進制解碼
三、發送發url編碼,接收方直接接收不用url解碼。
其實最開始我是用了url編碼的,但是結果也是空格,我就放棄了,后來偶然發現,可以直接接收單無需解碼。當然,服務端不同可能結果不一樣。
token = URLEncoder.encode(token,"utf-8");//發送方Url編碼