在进行3DES加密时当最后一位不足64时会自动补上,导致解密时报错
javax.crypto.BadPaddingException: Given final block not properly padded
我的结局方法是对3DES加密报文进行Base64编码,然后转为String传输,接收到的Sring转为byte[],先进性Base64解码,再进行3DES解码,就可以解决这个问题了。
大致逻辑:对原文进行3DES加密,由于存在最后一位不足64时的自动填充,导致解密时会报错,所以对可能进行了填充(也可能没填充,看最后一位)的3DES密文进行Base64编码,这时可以直接把Base64编码结果转为String进行传输,在其它地方接收到该String,利用getBytes("UTF-8"),得到byte[],进行Base64解码,对解码得到的byte数组进行3DES解密就可以得到原文了。
我这里只写了一个类,但我确实是把base64结果转为了String的,再次转为byte数组后才进行的解密。
并且我也用了原来会报错的报文例子进行的测试。
ps:文末有补充基于org.apache.commons.codec.binary.Base64实现的base64编码,因为有时候因为jdk版本问题,导致java.util.Base64报错,所以需要这种方法,不过两者是等效的(我在webservice服务端编码时使用了java.util.Base64,而客户端解码使用的是org.apache.commons.codec.binary.Base64,结果完全一致)
package com.server.model;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SecretTools {
//3DES
private static final String algorithm = "DESede";
private static final String pass_secretkey = "ThisIsAnExampleSecretKeyTest";
//Base64
private static final Base64.Decoder decoder = Base64.getDecoder();
private static final Base64.Encoder encoder = Base64.getEncoder();
//Test
public static void main(String[] args){
//待加密密文
String data = "ddbfdskfdsfslksjkffj";
System.out.println("原始报文内容:"+data);
//加密结果
//加密结果转为了String进行传输,
//调用加密的方法,得到了String形式的Base64密文
//此处Encryption方法中对3DES结果进行了Base64编码
String b = SecretTools.Encryption(data.getBytes());
//测试点,测试转为String是否导致byte填补丢失
//传输结果转为byte[]进行解密
//Decrypt方法中先进行了Base64解码
//再进行了3DES解密
//doFinal最终得到一个byte[],转为String输出即可得知与原文相同
byte[] c = SecretTools.Decrypt(b);
try {
System.out.println("最终解密报文:"+new String(c,"UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//加密算法
public static String Encryption(byte[] data){
try {
SecretKey deskey = new SecretKeySpec(creatdeskey(pass_secretkey),algorithm);
Cipher ch = Cipher.getInstance(algorithm);
ch.init(Cipher.ENCRYPT_MODE, deskey);
byte[] result = ch.doFinal(data);
System.out.println("3DES对原报文进行加密加密报文:"+new String(result));
//返回报文经过base64编码
return SecretTools.aBase64_encript(result);
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (NoSuchPaddingException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}catch(Exception e3){
e3.printStackTrace();
}
return null;
}
//解密算法
public static byte[] Decrypt(String data){
try {
//先进行base64解码
byte[] da = SecretTools.aBase64_decritp(data);
SecretKey deskey = new SecretKeySpec(creatdeskey(pass_secretkey),algorithm);
Cipher ch = Cipher.getInstance(algorithm);
ch.init(Cipher.DECRYPT_MODE, deskey);
byte[] des = ch.doFinal(da);
System.out.println("3DES解密获得原报文:"+new String(des));
return des;
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (NoSuchPaddingException e2) {
e2.printStackTrace();
}catch (Exception e3) {
e3.printStackTrace();
}
return null;
}
//Base64解码方法
public static byte[] aBase64_decritp(String encodedText){
//Base64解碼
try {
byte[] textByte = encodedText.getBytes("UTF-8");
byte[] str = decoder.decode(encodedText);
System.out.println("Base64解码,得到原始的3DES编码结果:"+new String(str));
//返回解码结果用于3DES解码
return str;
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//返回解码结果
return null;
}
//Base64编码方法
public static String aBase64_encript(byte[] textByte){
String encodedText = encoder.encodeToString(textByte);
//输出对应编码结果
System.out.println("Base64编码用于传输,编码结果:"+encodedText);
//把编码结果返回
//此编码结果已经转化为string型
return encodedText;
}
//把密钥约定为24位
public static byte[] creatdeskey(String passkey)throws UnsupportedEncodingException{
byte[] temp = passkey.getBytes();
byte[] truekey = new byte[24];
if(temp.length < truekey.length){
System.arraycopy(temp, 0, truekey, 0, temp.length);
}
else{
System.arraycopy(temp, 0, truekey, 0, truekey.length);
}
return truekey;
}
}
原始报文内容:ddbfdskfdsfslksjkffj
3DES对原报文进行加密加密报文:夁匽啸qb塟昩&u駄c7猔蝴
Base64编码用于传输,编码结果:id+FXdClcWKJWpViJhwPdfFqYzeqYLr7
Base64解码,得到原始的3DES编码结果:夁匽啸qb塟昩&u駄c7猔蝴
3DES解密获得原报文:ddbfdskfdsfslksjkffj
最终解密报文:ddbfdskfdsfslksjkffj
补充一下:
可能会存在因为版本问题导致java.util.Base64不能使用,这种情况你可以使用Apache Commons Codec进行base64编码
具体类是:
org.apache.commons.codec.binary.Base64
下载地址: http://commons.apache.org/proper/commons-codec/download_codec.cgi
如果你是用maven的话:
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
修改后的Base64加解密方法:
//base64解码
public static byte[] aBase64_decritp(String encodedText){
org.apache.commons.codec.binary.Base64 base = new org.apache.commons.codec.binary.Base64();
byte[] str=base.decode(encodedText);
return str;
}
//base64编码
public static String aBase64_encript(byte[] textByte){
org.apache.commons.codec.binary.Base64 base = new org.apache.commons.codec.binary.Base64();
String encodedText = base.encodeToString(textByte);
return encodedText;
}