簡述
筆者工作中常用的Base64算法的實現有三種方式,第一種是sun公司提供的Base64算法,第二種是bouncycastle提供的加密算法(以下簡稱BC包),第三種是apache的codec包(以下簡稱codec包)不推薦使用第一種,這些api在未來的jdk發行版本中是可能被刪除的。
Base64
背景:兩個公司直接互相通信,雙方約定加密方式,涉及數字簽名、數字證書等,約定非敏感信息使用Base64算法編碼。我方公司選用bouncycastle包的base64算法,對方公司選擇了codec包的base64加密算法。
/**
* 我方公司加密方法
* @param str
* @return
* @throws Exception
*/
private static String bouncycastleBase64Test(String str) throws Exception {
byte[] bcBase64 = org.bouncycastle.util.encoders.Base64.encode(str.getBytes());
String code = new String(bcBase64);
System.out.println("BC包默認: " + code);
return code;
}
/**
* 對方公司解密方法
* @param str
* @return
* @throws Exception
*/
private static String codecBase64Test(String str) throws Exception {
byte[] bcBase64 = org.apache.commons.codec.binary.Base64.encodeBase64(str.getBytes(), true);
String code = new String(bcBase64);
System.out.println("codec包默認:" + code);
return code;
}
public static void main(String[] args) throws Exception {
String str = "加密字符串";
String s1 = bouncycastleBase64Test(str);
String s2 = codecBase64Test(str);
System.out.println(s1.equals(s2));
}
運行結果:同樣是base54加密算法,得出的結果竟然不一致
字符串完全一樣,通過debug可以發現codec下面有一個換行符!
這是因為codec提供了RFC2045標準的Base64實現,每76個字符加一個換行符,如果當前行不足76個字符,也要在最后加上加換行符!上面代碼codec的Base64實現方法encodeBase64(str.getBytes(), true),第二個boolean類型的參數的意思是:是否使用RFC2045標準的Base64實現,也就是說如果為true,最后是有換行符的,如果為false,最后就沒有換行符!
Base64編碼
背景:我方公司和對方公司同樣使用同一個算法,比如bouncycastle的Base64編碼,但是雙方編碼是不一致的,同樣會導致加密得出的編碼不一致。
public static void main(String[] args) throws Exception {
String str = "加密字符串111";
bouncycastleBase64WithUtf8Test(str);
bouncycastleBase64WithGBKTest(str);
}
private static void bouncycastleBase64WithUtf8Test(String str) throws Exception {
byte[] bcBase64 = org.bouncycastle.util.encoders.Base64.encode(str.getBytes("UTF-8"));
String code = new String(bcBase64);
System.out.println("BC包utf8編碼:" + code);
}
private static void bouncycastleBase64WithGBKTest(String str) throws Exception {
byte[] bcBase64 = org.bouncycastle.util.encoders.Base64.encode(str.getBytes("GBK"));
String code = new String(bcBase64);
System.out.println("BC包GBK編碼:" + code);
}
輸出結果:
BC包utf8編碼:5Yqg5a+G5a2X56ym5LiyMTEx
BC包GBK編碼:vNPD3NfWt/u0rjExMQ==
同樣,使用不同字符編碼的加密解密也是有問題的,如下面代碼所示:
private static void bouncycastleBase6Test(String str) throws Exception {
byte[] encodeBase64 = org.bouncycastle.util.encoders.Base64.encode(str.getBytes("UTF-8"));
String code = new String(encodeBase64);
System.out.println("BC包utf8編碼:" + code);
byte[] decodeBase64 = org.bouncycastle.util.encoders.Base64.decode(str.getBytes("GBK"));
System.out.println("BC包GBK解碼:" + decodeBase64);
}
使用utf-8編碼,使用gbk解碼,在base64解密的時候報錯了
BC包utf8編碼:5Yqg5a+G5a2X56ym5LiyMTEx
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -68
at org.bouncycastle.util.encoders.Base64Encoder.decode(Unknown Source)
at org.bouncycastle.util.encoders.Base64.decode(Unknown Source)
at com.jiupai.wallstreet.biz.Test.bouncycastleBase64WithUtf8Test(Test.java:93)
at com.jiupai.wallstreet.biz.Test.main(Test.java:26)
URLBase64
Base64中的“/”和“=”在url中使用是可能出現問題的,所以BC包和codec包采取了不同的應對方式,BC包使用了.來作為填充符號,而codec包直接放棄了填充符號。
如下代碼:
private static void test3(String str) throws UnsupportedEncodingException {
String str = "加密字符……%&*1";
System.out.println(str);
byte[] bytes = org.bouncycastle.util.encoders.UrlBase64.encode(str.getBytes("UTF-8"));
byte[] s1 = org.apache.commons.codec.binary.Base64.encodeBase64URLSafe(str.getBytes("UTF-8"));
System.out.println("bouncycastle URLbase64加密后:"+new String(bytes));
System.out.println("codec URLbase64加密后 :"+new String(s1));
byte[] bytes4 = org.bouncycastle.util.encoders.UrlBase64.decode(s1);
byte[] bytes3 = org.apache.commons.codec.binary.Base64.decodeBase64(bytes);
System.out.println("使用codec URLbase64加密,使用BC URLbase64加密解密:" + new String(bytes4));
System.out.println("使用BC URLbase64加密,使用codec URLbase64加密解密:" + new String(bytes3));
}
結果:
加密字符……%&*1
bouncycastle URLbase64加密后:5Yqg5a-G5a2X56ym4oCm4oCmJSYqMQ..
codec URLbase64加密后 :5Yqg5a-G5a2X56ym4oCm4oCmJSYqMQ
使用codec URLbase64加密,使用BC URLbase64加密解密:加密字符……%&*b??
使用BC URLbase64加密,使用codec URLbase64加密解密:加密字符……%&*1
可以看到BC包使用了“.”作為填充符號,而codec包卻沒有使用填充符號,這時使用codec的URLBase64編碼,使用BC URLbase64加密解碼是可能出問題的,而使用BC包的URLbase64編碼,使用codec URLbase64解碼沒有出問題。
總結
1、使用BC包Base64加密,使用codec包的RFC2045標準的Base64方法解密,會導致解密不一致。
2、即使是同一種Base64加密算法,不同字符編碼會導致生成的密文不一致。
3、即使是同一種Base64加密算法,使用不同字符編碼加密解密會出問題。
4、對于URLBase64編碼,BC包和codec包的實現不同,對于生成的密文,BC包的密文最后可能會有填充符號“.”,而codec舍棄了填充符號,會導致不一致,有趣的是:使用codec包的URLBase64編碼,使用BC包的URLbase64解碼可能出問題;使用BC包的URLbase64編碼,使用codec URLbase64解碼不會出問題。