base64原理與運用
什麼是base64
Base64 編碼,首先我們應該搞清楚,為什麼裡面有個 64 的字樣呢?其實是因為該編碼使用 64 個明文來編碼任意 的二進位制檔案,它裡面只使用了 A-Z,a-z,0-9,+,/這 64 個字元,有“略懂”的同學就會說了,裡面還有“=”號啊,不錯,不過等號不屬於編碼字元,而是填充字元。
Base64是網路上最常見的用於傳輸8Bit位元組碼的編碼方式之一,Base64就是一種基於64個可列印字元來表示二進位制資料的方法。
用記事本開啟exe
、jpg
、pdf
這些檔案時,我們都會看到一大堆亂碼,因為二進位制檔案包含很多無法顯示和列印的字元,所以,如果要讓記事本這樣的文字處理軟體能處理二進位制資料,就需要一個二進位制到字串的轉換方法。Base64是一種最常見的二進位制編碼方法。
Base64編碼是從二進位制到字元的過程,可用於在http環境下傳遞較長的標識資訊。
如下:
誕生原因:
為什麼發明這麼個編碼呢,其實這個編碼的原理是很簡單的,“破解”也很容易,電子郵件剛出來的 時候,只傳遞英文字元,這沒有問題,但是後來,中國人,日本人都要發 email,這樣問題就來了,因為這些字元有可能會被郵件伺服器或者閘道器當成命令處 理,故必須得有一種編碼來對郵件進行加密,但是加密的目的是為了能夠使得一些原始的伺服器不出問題(新得牛叉伺服器已經能處理這些亂七八糟得情況了,不過 因為已經形成了一套規範,所以郵件還是得經過 Base64 編碼才能傳遞),這樣加密必須得簡單(那搞個取反,異或加密吧,還是沒解決根本問題 咯),加密簡單,這樣客戶端程式加密解密也快,又要是明文 Ascii 編碼,這樣 Base64 就誕生了。
編碼原理:
64是2的6次方,所以用6bit來表示一個base64字元,然而ASCII碼需要8個Bit來表示,所以正常來轉換是以3個位元組為單位,這樣3*8=4*6三個字元就轉換 成了4個base64字元,長度增加33%,好處是編碼後的文字資料可以在郵件正文、網頁等直接顯示。
以字串"Son"為例,通過base64編碼後就是:U29u,如下圖
剛剛好的情況,3個ASCII字元剛好轉換成對應的4個Base64字元。但是,當需要轉換的字元數不是3的倍數的情況下該怎麼辦呢?Base64規定,當字元數不是 3 的整數倍時,字元數/3 的餘數自然就是 2 或者 1。轉換的時候,結果不夠 6 位的用 0 來補上相應的位置,之後再在 6 位的前面補兩個 0。轉換完空出的結果就用 就用“=”來補位
每6個Bit為一組,第一組轉換後為字元“U”,第二組末尾補4個0轉換後為字元“w”。剩下的使用“=”替代。即字元“S”通過Base64編碼後為“Uw==”。這就是Base64的編碼過程。
線上Base64轉換器
Java中實現Base64
實現方式有3種(3中方式編碼出來的結果都是一樣的),任選一種即可,正常專案中用得比較多的是用Apache Commons Codec提供的實現.
1.jdk自帶的方式
2.(第三方)Apache Commons Codec提供的實現,需要引入依賴:
<!-- Apache Commons Codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
3.(第三方)bouncy Castle提供的實現,需要引入依賴:
<!-- bouncy castle -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
程式碼實現,我這裡三種方式都有提供,各位按需取用:
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Base64Demo {
private static String src = "Son";
public static void main(String[] args) {
System.out.println(Arrays.toString("0".getBytes()));
jdkBase64();
System.out.println("------------------");
commonsCodesBase64();
System.out.println("------------------");
bouncyCastleBase64();
}
/**
* jdk自帶的實現
*/
@SuppressWarnings("restriction")
public static void jdkBase64() {
try {
BASE64Encoder encoder = new BASE64Encoder();
String encode = encoder.encode(src.getBytes());
System.out.println("encode : " + encode);
BASE64Decoder decoder = new BASE64Decoder();
System.out.println("decode : " + new String(decoder.decodeBuffer(encode)));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* (第三方)Apache Commons Codec提供的實現
*/
public static void commonsCodesBase64() {
byte[] encodeBytes = Base64.encodeBase64(src.getBytes());
System.out.println("encode : " + new String(encodeBytes));
byte[] decodeBytes = Base64.decodeBase64(encodeBytes);
System.out.println("decode : " + new String(decodeBytes));
}
/**
* (第三方)bouncy Castle提供的實現
*/
public static void bouncyCastleBase64() {
byte[] encodeBytes = org.bouncycastle.util.encoders.Base64.encode(src.getBytes());
System.out.println("encode : " + new String(encodeBytes));
byte[] decodeBytes = org.bouncycastle.util.encoders.Base64.decode(encodeBytes);
System.out.println("decode : " + new String(decodeBytes));
}
}
帶來的一些問題:
1.由於標準的Base64編碼後可能出現字元+
和/
,在URL中就不能直接作為引數,所以又有一種"url safe"的base64編碼,其實就是把字元+
和/
分別變成-
和_
:
/**
* URL安全的base64編碼
*/
public static void commonsCodesBase64URLSafe() {
// encoder will emit - and _ instead of the usual + and / characters
String encode = Base64.encodeBase64URLSafeString("1245".getBytes());
System.out.println(encode);
byte[] decode = Base64.decodeBase64(encode);
System.out.println(new String(decode));
}
2.由於=
字元也可能出現在Base64編碼中,但=
用在URL、Cookie裡面會造成歧義,所以,很多Base64編碼後要把=
去掉,這裡可以用URLEncoder和URLDecoder來解決.
public static void commonsCodesBase64CookieSafe() throws UnsupportedEncodingException {
byte[] encodeBytes = Base64.encodeBase64("12".getBytes());
System.out.println("base64Encode : " + new String(encodeBytes));
String encode = URLEncoder.encode(new String(encodeBytes), "UTF-8");
System.out.println("URLEncoder : " +encode);
String decode = URLDecoder.decode(encode, "UTF-8");
System.out.println("URLDecoder : " +decode);
byte[] decodeBytes = Base64.decodeBase64(decode);
System.out.println("base64Decode : " + new String(decodeBytes));
}
輸出結果:
base64Encode : MTI=
URLEncoder : MTI%3D
URLDecoder : MTI=
base64Decode : 12
一些使用案例
先以“迅雷下載”為例: 很多下載類網站都提供“迅雷下載”的連結,其地址通常是加密的迅雷專用下載地址。
其實迅雷的“專用地址”也是用Base64"加密"的,其過程如下:
一、在地址的前後分別新增AA和ZZ
二、對新的字串進行Base64編碼
另: Flashget的與迅雷類似,只不過在第一步時加的“料”不同罷了,Flashget在地址前後加的“料”是[FLASHGET]
而QQ旋風的乾脆不加料,直接就對地址進行Base64編碼了
參考資料:
https://baike.baidu.com/item/base64