解決IllegalBlockSizeException:last block incomplete in decryption異常
分類: webkit android
最近作個加解密的實現,雖然實現了,可是發現還有以下的異常出現:
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:711)
at javax.crypto.Cipher.doFinal(Cipher.java:1090)
問題緣由:
多是由於直接將一個string的byte []字符串直接加密成密文,在傳輸過程當中,因爲默認的編碼方式的問題可能會形成數據的丟失。(若是有更好的解釋,歡迎指出)
解決方法:
將加密後的密文再進行總體的base64加密,解碼時先對其進行base64解密再進DES/AES解密,這樣就能保證接受數據的正確性而且不會缺失。java
Base64Utils加密工具android
package test; import it.sauronsoftware.base64.Base64; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; /** *//** * <p> * BASE64編碼解碼工具包 * </p> * <p> * 依賴javabase64-1.3.1.jar * </p> * * @author IceWee * @date 2012-5-19 * @version 1.0 */ public class Base64Utils { private static String SF_DF_BASE64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";//自定義時解碼使用 /** *//** * 文件讀取緩衝區大小 */ private static final int CACHE_SIZE = 1024; /** *//** * <p> * BASE64字符串解碼爲二進制數據 * </p> * * @param base64 * @return * @throws Exception */ public static byte[] decode(String base64) throws Exception { return Base64.decode(base64.getBytes()); } /** * 自定義的解碼實現 * @param base64 * @return * @throws Exception */ public static byte[] selfDecode1(String base64)throws Exception { int n,i,j,pad; byte [] dst; char [] src; int len = 0; pad=0; n = base64.length(); src = new char [n]; for(i=0;i<n;i++){//複製到src中 src[i] = base64.charAt(i); } while(n>0&&src[n-1]=='=') { src[n-1]=0; pad++; n--; } for(i=0;i<n;i++) { //map base64 ASCII character to 6 bit value int iTt = SF_DF_BASE64.indexOf(src[i]); if(iTt<0) break; src[i] = (char)iTt; } dst = new byte[n*3/4+1]; for(i=0,j=0;i<n;i+=4,j+=3) { dst[j] = (byte)((src[i]<<2) + ((src[i+1]&0x30)>>4)); dst[j+1] = (byte)(((src[i+1]&0x0F)<<4) + ((src[i+2]&0x3C)>>2)); dst[j+2] = (byte)(((src[i+2]&0x03)<<6) + src[i+3]); len+=3; } len-=pad; return dst; } /** *//** * <p> * 二進制數據編碼爲BASE64字符串 * </p> * * @param bytes * @return * @throws Exception */ public static String encode(byte[] bytes) throws Exception { return new String(Base64.encode(bytes)); } /** *//** * <p> * 二進制數據編碼爲BASE64字符串 * </p> * * @param buf * @return * @throws Exception */ public static String selfEncode1(byte[] buf) throws Exception { int n,buflen,i,j; byte []dst = null; //CString buf = src; buflen=n=buf.length; dst = new byte[buflen/3*4+3]; for(i=0,j=0;i<=buflen-3;i+=3,j+=4) { dst[j] = (byte)((buf[i]&0xFC)>>2); dst[j+1] = (byte)(((buf[i]&0x03)<<4) + ((buf[i+1]&0xF0)>>4)); dst[j+2] = (byte)(((buf[i+1]&0x0F)<<2) + ((buf[i+2]&0xC0)>>6)); dst[j+3] = (byte)(buf[i+2]&0x3F); } if(n%3==1) { dst[j] = (byte)((buf[i]&0xFC)>>2); dst[j+1] = (byte)(((buf[i]&0x03)<<4)); dst[j+2]=64; dst[j+3]=64; j+=4; } else if(n%3==2) { dst[j] = (byte)((buf[i]&0xFC)>>2); dst[j+1] = (byte)(((buf[i]&0x03)<<4)+((buf[i+1]&0xF0)>>4)); dst[j+2] = (byte)(((buf[i+1]&0x0F)<<2)); dst[j+3]=64; j+=4; } for(i=0;i<j;i++) /* map 6 bit value to base64 ASCII character */ dst[i] = (byte)SF_DF_BASE64.charAt((int)dst[i]); dst[j]=0; return new String(dst); } /** *//** * <p> * 將文件編碼爲BASE64字符串 * </p> * <p> * 大文件慎用,可能會致使內存溢出 * </p> * * @param filePath 文件絕對路徑 * @return * @throws Exception */ public static String encodeFile(String filePath) throws Exception { byte[] bytes = fileToByte(filePath); return encode(bytes); } /** *//** * <p> * BASE64字符串轉回文件 * </p> * * @param filePath 文件絕對路徑 * @param base64 編碼字符串 * @throws Exception */ public static void decodeToFile(String filePath, String base64) throws Exception { byte[] bytes = decode(base64); byteArrayToFile(bytes, filePath); } /** *//** * <p> * 文件轉換爲二進制數組 * </p> * * @param filePath 文件路徑 * @return * @throws Exception */ public static byte[] fileToByte(String filePath) throws Exception { byte[] data = new byte[0]; File file = new File(filePath); if (file.exists()) { FileInputStream in = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(2048); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); data = out.toByteArray(); } return data; } /** *//** * <p> * 二進制數據寫文件 * </p> * * @param bytes 二進制數據 * @param filePath 文件生成目錄 */ public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception { InputStream in = new ByteArrayInputStream(bytes); File destFile = new File(filePath); if (!destFile.getParentFile().exists()) { destFile.getParentFile().mkdirs(); } destFile.createNewFile(); OutputStream out = new FileOutputStream(destFile); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); } // 加密 public static String getBase64(String str) { byte[] b = null; String s = null; try { b = str.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (b != null) { s = new BASE64Encoder().encode(b); } return s; } // 解密 public static String getFromBase64(String s) { byte[] b = null; String result = null; if (s != null) { BASE64Decoder decoder = new BASE64Decoder(); try { b = decoder.decodeBuffer(s); result = new String(b, "utf-8"); } catch (Exception e) { e.printStackTrace(); } } return result; } }
TestWebServiceweb
package test; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebService public class TestWebService { //解密 public String executeDe(String content){ String password = "1234567890123456"; byte[] decryptResult; String decrypt; try { //base64解密(後加!!) String decodeBase64 = Base64Utils.getFromBase64(content); decryptResult = Base64Utils.decode(decodeBase64); decrypt = new String(decrypt(decryptResult,password,16)); return decrypt; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } //加密 public String executeEn(String content){ System.out.println(System.getProperty("file.encoding")); //加密內容 //String content = "密碼學中的高級加密標準(AdvancedEncryptionStandard,AES)"; //String content = "rowvin"; //String content = "test123456"; //爲與Delphi編碼統一,將字符轉爲UTF8編碼(其餘語言也相同) //String ss=new String(content.getBytes(),"UTF-8"); //密鑰 String password = "1234567890123456"; System.out.println("加密前:" + content); byte[] encryptResult; String encrypt; try { encryptResult = encrypt(content, password,16);//16位密鑰長度128位、24位密鑰長度19二、32位密鑰長度256(在delphi中對應kb12八、kb19二、快播56) //System.out.println("加密後:" + parseByte2HexStr(encryptResult));//將加密後編碼二進制轉爲16進制編碼 System.out.println(Base64Utils.encode(encryptResult));//二進制轉Hbase64 encrypt = Base64Utils.encode(encryptResult); //base64加密(後加!) String encodeAfterBase64 = Base64Utils.getBase64(encrypt); StringBuffer AesBuff = new StringBuffer(); /*decrypt = new String(decrypt(encryptResult,password,16)); System.out.println("解密後:" + decrypt);*/ AesBuff.append("{"); AesBuff.append("\"encrypt\":" + "\"" + encrypt + "\","); AesBuff.append("\"encodeAfterBase64\":" + "\"" + encodeAfterBase64 + "\"}"); //System.out.println(json.toString()); return AesBuff.toString(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加密 * * @param content 須要加密的內容 * @param password 加密密碼 * @param keySize 密鑰長度16,24,32 * @return * @throws UnsupportedEncodingException * @throws InvalidAlgorithmParameterException */ public byte[] encrypt(String content, String password, int keySize) throws UnsupportedEncodingException, InvalidAlgorithmParameterException { try { //密鑰長度不夠用0補齊。 SecretKeySpec key = new SecretKeySpec(ZeroPadding(password.getBytes(), keySize), "AES"); //定義加密算法AES、算法模式ECB、補碼方式PKCS5Padding //Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //定義加密算法AES 算法模式CBC、補碼方式PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //CBC模式模式下初始向量 不足16位用0補齊 IvParameterSpec iv = new IvParameterSpec(ZeroPadding("1234567890123456".getBytes(),16)); byte[] byteContent = content.getBytes(); //初始化加密 //ECB //cipher.init(Cipher.ENCRYPT_MODE, key); //CBC cipher.init(Cipher.ENCRYPT_MODE, key,iv); byte[] result = cipher.doFinal(byteContent); return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /**解密 * @param content 待解密內容 * @param password 解密密鑰 * @param keySize 密鑰長度16,24,32 * @return * @throws InvalidAlgorithmParameterException */ public byte[] decrypt(byte[] content, String password, int keySize) throws InvalidAlgorithmParameterException { try { //密鑰長度不夠用0補齊。 SecretKeySpec key = new SecretKeySpec(ZeroPadding(password.getBytes(), keySize), "AES"); //定義加密算法AES、算法模式ECB、補碼方式PKCS5Padding //Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //定義加密算法AES 算法模式CBC、補碼方式PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //CBC模式模式下初始向量 不足16位用0補齊 IvParameterSpec iv = new IvParameterSpec(ZeroPadding("1234567890123456".getBytes(),16)); // 初始化解密 //ECB //cipher.init(Cipher.DECRYPT_MODE, key); //CBC cipher.init(Cipher.DECRYPT_MODE, key,iv); byte[] result = cipher.doFinal(content); return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /**將二進制轉換成16進制 * @param buf * @return */ public String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /**將16進制轉換爲二進制 * @param hexStr * @return */ public byte[] parseHexStr2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length()/2]; for (int i = 0;i< hexStr.length()/2; i++) { int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); result[i] = (byte) (high * 16 + low); } return result; } public byte[] ZeroPadding(byte[] in,Integer blockSize){ Integer copyLen = in.length; if (copyLen > blockSize) { copyLen = blockSize; } byte[] out = new byte[blockSize]; System.arraycopy(in, 0, out, 0, copyLen); return out; } public static void main(String[] args) { Endpoint.publish("http://10.80.3.51:9999/Service/TestWebService", new TestWebService()); System.out.println("服務已啓動~~~~"); } }