Base64 原理

detectiveHLH發表於2021-08-24

Base64

Base64 是什麼?是將位元組流轉換成可列印字元、將可列印字元轉換為位元組流的一種演算法。Base64 使用 64 個可列印字元來表示轉換後的資料。

準確的來說,Base64 不算是一種加、解密的演算法,它是一種編碼、解碼的演算法。這也是為什麼我的用詞是編碼、解碼,而不是加密、解密。

編碼原理

這裡的討論的前提是使用 UTF-8 編碼

Base64 演算法的原理,是將輸入流中的位元組按每 3 個分為一組,然後每次取 6 個位元,將其轉換成表格中對應的資料,一直重複到沒有剩餘的字元為止,轉換表格如下:

Index Character Index Character Index Character Index Character
0 A 1 B 2 C 3 D
4 E 5 F 6 G 7 H
8 I 9 J 10 K 11 L
12 M 13 N 14 O 15 P
16 Q 17 R 18 S 19 T
20 U 21 V 22 W 23 X
24 Y 25 Z 26 a 27 b
28 c 29 d 30 e 31 f
32 g 33 h 34 i 35 j
36 k 37 l 38 m 39 n
40 o 41 p 42 q 43 r
44 s 45 t 46 u 47 v
48 w 49 x 50 y 51 z
52 0 53 1 54 2 55 3
56 4 57 5 58 6 59 7
60 8 61 9 62 + 63 /

編碼過程

舉個例子,假設我們要對字串 S.H 進行編碼:

  1. 將其轉換成十六進位制為 53、2e、48

  2. 再將十六進位制轉換成二進位制,分別為 010100110010111001001000。這裡不足 8 個位元的高位補 0 即可。

  3. 將其每6個位元分為一組,分別為 010100110010111001001000

  4. 將其轉換成十進位制得到,20、50、57、8

  5. 再根據表格中的轉換關係轉換可得,U、y、5、I

換句話說,字串 S.H 通過 Base64 演算法編碼之後的結果為 Uy5I

編碼圖解

如果覺得文字較難理解,我把上面的流程用圖的形式畫了出來,可以結合著一起看。

為什麼要 每三個 分為一組,因為 3 8 = 24,24 = 4 ​ 6,這樣子可以剛好可以均分完。

那如果我輸入的位元組不足三個呢?

例如 SH ?按照上述的做法:

首先將其轉換成十六進位制5348,再將其轉換成二進位制0101001101001000,再按照每 6 個位元分為一組,就會變成 0101001101001000,再轉換成十進位制得到 20、52、8,最後得到 U0I.

然而這個結果是不正確的,隨便去找一個工具輸入轉換看看都知道,最終結果為 U0g=. 這也說明在輸入的字元不足 3 個時,就不是按照之前的方式來處理了。

不足三個位元組如何處理?

假設需要編碼的字串還是 SH

將其轉換成二進位制為, 0101001101001000,再按照每 6 個位元分為一組,就會變成0101001101001000

但是可以看到最後一組的位元位不足 6 個,在這種情況下,會進行末尾(低位)補0的操作。補完之後就會變成010100110100100000。但是你會發現,這裡總共也只有18個位元,不滿足 3 個位元組一組的原則。在這種情況下,前三組會按照常規的 Base64 進行編碼,而缺失的一組則會使用 = 來進行填充。

這樣一來,就會變成205232,再根據表格轉換可得 U0g ,再加上最後填充的 = ,最終結果就是 U0g=.

以下是圖解。

只有一個位元組如何處理?

那同理,如果只有一個字元,最後在二進位制分組的時候,不足 6 位的低位補 0,分組不滿 4 的,直接以 = 號填充。舉個例子,假設需要編碼的是字串 S

S 的二進位制為 01010011 ,按照 6 個位元分為一組,01010011。第二組明顯不滿 6 個位元,進行低位補0操作。

低位補0之後結果變成了010100110000,這裡只有 2 組,不滿四組,所以這裡需要填充 2 個 =。將前面的兩組轉換成字元,結果為 Uw,再結合填充字元,最終的結果為 Uw==

關於編碼,有人可能會說,你這都是英文,英文轉換成十進位制再到十六進位制很方便,對比 ASCII 碼就行,那要是中文呢?實際上,這個跟採取的編碼類別有關係。對同樣的中文采用不同的編碼,最後得到的結果可能都不同。所以我們這裡只討論採用 UTF-8 的場景。

如果是中文,就採用 UTF-8 將中文進行編碼,而如果是英文,其轉換結果和 ASCII 編碼是一樣的。

解碼原理

因為最終的編碼產物中,如果 6 個位元的分組不滿 4 組,會有 = 作為填充物,所以一個 base64 完後的產物總是能夠被 4 整除。

所以,在解密中,我們每次需要處理 4 個字元,將這 4 個字元編碼之後轉換成十進位制,再轉換成二進位制,不足 6 位的高位補0,然後將 6 個位元一組的二進位制數按原順序重新分成每 8 個位元一組,也就是一個位元組一組。然後將其轉換成十六進位制,再轉換成對應的字元。

解碼過程

假設我們需要解密的字元為 Uy5I

解密過程就會像:

  1. 按照每次處理4個字元的原理,根據表格將其分別轉換成十進位制2050578
  2. 再將其轉換成二進位制,不足六位的高位補0,再將其分成每 8 個位元一組
  3. 將分組好的位元轉換成十六進位制,得到532e48
  4. 最後將十六進位制轉換成字母得到S.H,也就是 S.H

解碼圖解

換成圖片來說就是如下這樣

這裡我們處理的是一個比較理想的情況,因為所有的位元位剛好被填充完,那如果帶有 = padding 的 base64 是如何進行解密的呢?

這裡拿 SH 編碼之後的 base64 字串 U0g= 來做例子

  1. 首先根據表格,將其轉換成十進位制205032
  2. 再將其轉換成二進位制,不足 6 個位元的高位補0,010100110100100000
  3. 再將其分成每 8 個位元位一組,0101001101001000
  4. 然後再轉換成十六進位制得5348
  5. 轉換成字串可得 SH

本篇文章已放到我的 Github github.com/sh-blog 中,歡迎 Star。微信搜尋關注【SH的全棧筆記】,回覆【佇列】獲取MQ學習資料,包含基礎概念解析和RocketMQ詳細的原始碼解析,持續更新中。

如果你覺得這篇文章對你有幫助,還麻煩點個贊關個注分個享留個言