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
進行編碼:
將其轉換成十六進位制為 53、2e、48
再將十六進位制轉換成二進位制,分別為
01010011
、00101110
、01001000
。這裡不足 8 個位元的高位補 0 即可。將其每6個位元分為一組,分別為
010100
、110010
、111001
、001000
將其轉換成十進位制得到,20、50、57、8
再根據表格中的轉換關係轉換可得,U、y、5、I
換句話說,字串 S.H
通過 Base64 演算法編碼之後的結果為 Uy5I
。
編碼圖解
如果覺得文字較難理解,我把上面的流程用圖的形式畫了出來,可以結合著一起看。
為什麼要 每三個 分為一組,因為 3 8 = 24,24 = 4 6,這樣子可以剛好可以均分完。
那如果我輸入的位元組不足三個呢?
例如 SH
?按照上述的做法:
首先將其轉換成十六進位制53
、48
,再將其轉換成二進位制01010011
、01001000
,再按照每 6 個位元分為一組,就會變成 010100
、110100
、1000
,再轉換成十進位制得到 20、52、8,最後得到 U0I
.
然而這個結果是不正確的,隨便去找一個工具輸入轉換看看都知道,最終結果為 U0g=
. 這也說明在輸入的字元不足 3 個時,就不是按照之前的方式來處理了。
不足三個位元組如何處理?
假設需要編碼的字串還是 SH
。
將其轉換成二進位制為, 01010011
、01001000
,再按照每 6 個位元分為一組,就會變成010100
、110100
、1000
。
但是可以看到最後一組的位元位不足 6 個,在這種情況下,會進行末尾(低位)補0的操作。補完之後就會變成010100
、110100
、100000
。但是你會發現,這裡總共也只有18個位元,不滿足 3 個位元組一組的原則。在這種情況下,前三組會按照常規的 Base64 進行編碼,而缺失的一組則會使用 =
來進行填充。
這樣一來,就會變成20
、52
、32
,再根據表格轉換可得 U0g
,再加上最後填充的 =
,最終結果就是 U0g=
.
以下是圖解。
只有一個位元組如何處理?
那同理,如果只有一個字元,最後在二進位制分組的時候,不足 6 位的低位補 0,分組不滿 4 的,直接以 =
號填充。舉個例子,假設需要編碼的是字串 S
。
S
的二進位制為 01010011
,按照 6 個位元分為一組,010100
、11
。第二組明顯不滿 6 個位元,進行低位補0操作。
低位補0之後結果變成了010100
、110000
,這裡只有 2 組,不滿四組,所以這裡需要填充 2 個 =
。將前面的兩組轉換成字元,結果為 Uw
,再結合填充字元,最終的結果為 Uw==
。
關於編碼,有人可能會說,你這都是英文,英文轉換成十進位制再到十六進位制很方便,對比 ASCII 碼就行,那要是中文呢?實際上,這個跟採取的編碼類別有關係。對同樣的中文采用不同的編碼,最後得到的結果可能都不同。所以我們這裡只討論採用 UTF-8
的場景。
如果是中文,就採用 UTF-8
將中文進行編碼,而如果是英文,其轉換結果和 ASCII 編碼是一樣的。
解碼原理
因為最終的編碼產物中,如果 6 個位元的分組不滿 4 組,會有 =
作為填充物,所以一個 base64 完後的產物總是能夠被 4 整除。
所以,在解密中,我們每次需要處理 4 個字元,將這 4 個字元編碼之後轉換成十進位制,再轉換成二進位制,不足 6 位的高位補0,然後將 6 個位元一組的二進位制數按原順序重新分成每 8 個位元一組,也就是一個位元組一組。然後將其轉換成十六進位制,再轉換成對應的字元。
解碼過程
假設我們需要解密的字元為
Uy5I
解密過程就會像:
按照每次處理4個字元的原理,根據表格將其分別轉換成十進位制 20
、50
、57
、8
再將其轉換成二進位制,不足六位的高位補0,再將其分成每 8 個位元一組 將分組好的位元轉換成十六進位制,得到 53
、2e
、48
最後將十六進位制轉換成字母得到 S
、.
、H
,也就是S.H
解碼圖解
換成圖片來說就是如下這樣
這裡我們處理的是一個比較理想的情況,因為所有的位元位剛好被填充完,那如果帶有 =
padding 的 base64 是如何進行解密的呢?
這裡拿
SH
編碼之後的 base64 字串U0g=
來做例子
首先根據表格,將其轉換成十進位制 20
、50
、32
再將其轉換成二進位制,不足 6 個位元的高位補0, 010100
、110100
、100000
再將其分成每 8 個位元位一組, 01010011
、01001000
、然後再轉換成十六進位制得 53
、48
轉換成字串可得 SH
本篇文章已放到我的 Github github.com/sh-blog 中,歡迎 Star。微信搜尋關注【SH的全棧筆記】,回覆【佇列】獲取MQ學習資料,包含基礎概念解析和RocketMQ詳細的原始碼解析,持續更新中。
如果你覺得這篇文章對你有幫助,還麻煩點個贊,關個注,分個享,留個言。