天天看點

BASE64的介紹及用Java語言編寫代碼實作

作者:程式設計實踐
BASE64的介紹及用Java語言編寫代碼實作

1、引言

說到加密解密,很多書籍都會把BASE64扯進來,并且和MD5、SHA放在一起講解,作為不可逆加密方案的一部分。

但這些書籍,在描述BASE64時,又會特别指出,BASE64并不是不可逆加密方案,是可逆的,并且根本算不上加密。

本質上來說,BASE64隻是将二進制資料(包括字元資料),換了一種編碼方式,變成了人不可以直接閱讀的文本。

2、BASE64的定義

BASE64是在RFC2045中定義的,大家在百度中輸入“RFC2045”,即可找到該文檔的全文。

RFC2045用于定義MIME(多用途網際網路郵件擴充協定),定義了消息體的格式。該文檔的6.8節(Base64 Content-Transfer-Encoding)較長的描述了BASE64編碼的規則。

BASE64的編碼的較長的描述如下:

(1)可以對任意的位元組序列進行編碼;

(2)編碼的結果比源碼長33%;

(3)BASE64使用了65個ASCII字元來表示編碼結果;

(4)BASE64中的64個字元代表6bit的值;

(5)BASE64除了表示值的64個字元外,還有一個等号字元=,有特殊含義;

(6)BASE64将24bit,也就是3個位元組,變成4個字元;

(7)BASE64将原始資料當成大端格式的位元組序列,即第一個bit是高位,對應的英語表示是most-significant-bit first;

(8)6bit值與BASE64的字元映射關系如下:

BASE64的介紹及用Java語言編寫代碼實作

(9)BASE64編碼結果中的回車、換行以及其他字元,必須被忽略;

(10)輸入的位元組數組,按照每3位元組變成生成4個字元,如果最後不足3位元組,則沒法湊足成6bit的序列,不足的部分通過補充0來滿足;

(11)如果輸入的位元組數組,最後隻剩一個位元組未編碼,則隻能湊足兩個6bit,輸出的結果帶兩個等号==;

(12)如果輸入的位元組數組,最後隻剩兩個位元組未編碼,則隻能湊足三個6bit,輸出的結果帶一個等号=。

這樣流水賬式的描述,雖然挺繁瑣,卻是程式員最習慣的。

3、手工計算BASE64編碼值

如果我們對三位元組序列0x00 0x00 0x00進行BASE64編碼,相當于對四個6bit值 0,0,0,0進行映射轉換,結果自然是AAAA;

如果我們對兩位元組序列0x00 0x00進行BASE64編碼,相當于對補零之後的三個6bit值0,0,0進行映射轉換,結果是AAA=;

如果我們對單位元組0x00進行BASE64編碼,相當于對補零之後的兩個6bit值0,0進行映射轉換,結果是AA==;

如果對于非零的值,例如對三位元組序列0x01 0x01 0x01進行轉換,以位元組序列的視角來看,是這樣的:

BASE64的介紹及用Java語言編寫代碼實作

如果以6bit序列的視角來看,則是這樣的:

BASE64的介紹及用Java語言編寫代碼實作

相當于4個6bit的值 0,16,4,1,進行映射轉換,結果就是AQEB。

4、使用JDK的BASE64編碼功能

JDK提供了多個關于BASE64編碼的類,這裡使用BASE64Encoder類來完成。代碼如下:

package com.flying.base;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import sun.misc.BASE64Encoder;
@SpringBootApplication
public class BaseApplication {
public static void main(String[] args) {
SpringApplication.run(BaseApplication.class, args);
BASE64Encoder base64Encoder = new BASE64Encoder();
byte[] bytes;
bytes = new byte[]{0, 0, 0};
System.out.println("BASE64 of {0, 0, 0} is: " + base64Encoder.encode(bytes));
bytes = new byte[]{0, 0};
System.out.println("BASE64 of {0, 0} is: " + base64Encoder.encode(bytes));
bytes = new byte[]{0};
System.out.println("BASE64 of {0} is: " + base64Encoder.encode(bytes));
bytes = new byte[]{1, 1, 1};
System.out.println("BASE64 of {1, 1, 1} is: " + base64Encoder.encode(bytes));
}
}           

程式的運作結果為:

BASE64的介紹及用Java語言編寫代碼實作

可以看到,程式的運作結果和手工計算的結果相同。

5、自己寫代碼實作簡易的BASE64編碼功能

BASE64編碼的規則很簡單,我們完全可以自己實作一個簡易版本的,這是我臨時編寫的代碼,大家将就着看一下:

package com.flying.base;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BaseApplication {
public static void main(String[] args) {
SpringApplication.run(BaseApplication.class, args);
byte[] bytes;
bytes = new byte[]{0, 0, 0};
System.out.println("Self BASE64 of {0, 0, 0} is: " + base64Encode(bytes));
bytes = new byte[]{0, 0};
System.out.println("Self BASE64 of {0, 0} is: " + base64Encode(bytes));
bytes = new byte[]{0};
System.out.println("Self BASE64 of {0} is: " + base64Encode(bytes));
bytes = new byte[]{1, 1, 1};
System.out.println("Self BASE64 of {1, 1, 1} is: " + base64Encode(bytes));
}
private static char[] base64Characters ={
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
static String base64Encode(byte[] bytes){
if (bytes == null || bytes.length == 0){
return "";
}
int index1, index2, index3, index4;
StringBuilder stringBuilder = new StringBuilder();
int timesOfThree = bytes.length / 3;
for (int i=0; i<timesOfThree; i++){
index1 = bytes[i*3] >> 2;
index1 = index1 & 0x3F;
index2 = bytes[i*3] & 0x03;
index2 = index2 << 4;
index2 = index2 | ((bytes[i*3+1] & 0xF0) >> 4);
index2 = index2 & 0x3F;
index3 = bytes[i*3+1] & 0x0F;
index3 = index3 << 2;
index3 = index3 | ((bytes[i*3+2] & 0xC0) >> 6);
index3 = index3 & 0x3F;
index4 = bytes[i*3+2] & 0x3F;
stringBuilder.append(base64Characters[index1]).append(base64Characters[index2]).append(base64Characters[index3]).append(base64Characters[index4]);
}
int remainPos = bytes.length / 3;
remainPos = remainPos * 3;
switch (bytes.length % 3){
case 1:
index1 = bytes[remainPos*3] >> 2;
index1 = index1 & 0x3F;
index2 = bytes[remainPos*3] & 0x03;
index2 = index2 << 4;
index2 = index2 & 0x3F;
stringBuilder.append(base64Characters[index1]).append(base64Characters[index2]).append('=').append('=');
break;
case 2:
index1 = bytes[remainPos*3] >> 2;
index1 = index1 & 0x3F;
index2 = bytes[remainPos*3] & 0x03;
index2 = index2 << 4;
index2 = index2 | ((bytes[remainPos*3+1] & 0xF0) >> 4);
index2 = index2 & 0x3F;
index3 = bytes[remainPos*3+1] & 0x0F;
index3 = index3 << 2;
index3 = index3 & 0x3F;
stringBuilder.append(base64Characters[index1]).append(base64Characters[index2]).append(base64Characters[index3]).append('=');
break;
default:
break;
}
return stringBuilder.toString();
}
}           

程式的運作結果如下:

BASE64的介紹及用Java語言編寫代碼實作

上面的示例代碼,隻進行了BASE64的編碼,如果你感興趣,可以嘗試編寫BASE64解碼的程式。

繼續閱讀