1. 程式人生 > >base64原理與運用

base64原理與運用

什麼是base64

Base64 編碼,首先我們應該搞清楚,為什麼裡面有個 64 的字樣呢?其實是因為該編碼使用 64 個明文來編碼任意 的二進位制檔案,它裡面只使用了 A-Z,a-z,0-9,+,/這 64 個字元,有“略懂”的同學就會說了,裡面還有“=”號啊,不錯,不過等號不屬於編碼字元,而是填充字元。

Base64是網路上最常見的用於傳輸8Bit位元組碼的編碼方式之一,Base64就是一種基於64個可列印字元來表示二進位制資料的方法。

用記事本開啟exejpgpdf這些檔案時,我們都會看到一大堆亂碼,因為二進位制檔案包含很多無法顯示和列印的字元,所以,如果要讓記事本這樣的文字處理軟體能處理二進位制資料,就需要一個二進位制到字串的轉換方法。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。轉換完空出的結果就用 就用“=”來補位

,總之要保證最後編碼出來得位元組數是 4 的倍數。

每6個Bit為一組,第一組轉換後為字元“U”,第二組末尾補4個0轉換後為字元“w”。剩下的使用“=”替代。即字元“S”通過Base64編碼後為“Uw==”。這就是Base64的編碼過程。

線上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編碼後要把=去掉,這裡可以用URLEncoderURLDecoder來解決.

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

https://blog.csdn.net/qq_20545367/article/details/79538530

https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001399413803339f4bbda5c01fc479cbea98b1387390748000

https://cloud.tencent.com/developer/article/1134508