徹底弄懂 base64 及其原理


轉自 https://mp.weixin.qq.com/s/TcSNPY1a6z8kP76usH6dCA

Base32 與 Base64

Base32 是一個 binary-to-text encoding schemes,顧名思義,就是將二進制數據轉換為編碼只有基礎 32 個字符的數據編碼方式,Base64 則是 64 個。注意編碼不等同于加密,網上有誤解 Base 編碼方式為加密方式,實際上標準 Base64 編碼解碼無需額外信息即完全可逆。

Base 編碼常見用途如下

如定義所言,binary to text

一些協議如 HTTPFTP (File Transfer Protocol)[當指定發送文本時], SMTP (Simple Mail Transfer Protocol)text-based protocol,也就是只支持文本傳輸,不支持二進制傳輸。是的,http 上傳文件,圖片時使用的 multipart/form-data 也是需要轉成文本的。

所以附件如圖片,文件等(binary)就可以用 Base64 編碼為 text再傳輸。

將資源編碼為字符串

data URI scheme 定義了如下語法來識別網頁中的資源:

 data:[<media type>][;base64],<data>

HTML 中可以在標簽中指定識別 Base64編碼 來展示資源,

<div>
  <p>Taken from wikpedia</p>
  <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
</div>

但因為 Base64 是每 3 個原始字符編碼成 4 個字符,不夠時補 =(下文會詳述),因此編碼后的大小是有可能會比原文件大的,所以 html 用 Base64 來展示圖片而不是用具體的圖片好處大概就只有少建立一條 http 連接以及少一個 http 請求(在 HTTP 1.1 以下),這種辦法只有大量的小圖片才有優越性了。

統一轉成『合法』字符

為了避免出現不符合規則的字符,方便把含有不可見字符串的信息用可見字符串表示出來。比如 http 協議當中的 headers 頭部,必須進行 URLEncode 不然出現的等號可能使解析失敗,空格也會使 http 請求解析出現問題,比如請求行也就是 request 就是以空格來劃分的 POST /hi/you HTTP/1,值得注意的是 Base32 的字符列表里有不合法字符 /。

還有避免原始信息經過百花齊開的路由,網關多次轉發,因有部分系統不支持此不可識別字符或將此作為控制符,將其轉義、丟棄等,造成信息丟失,所以如電子郵件里的附件也是用 base64 編碼的。

base64url

有 base64 編碼的變種 base64url,將base64 編碼中的 + 換成 - 以及將 / 換成_,甚至不需要往后面補=了。這樣子在 url 中傳遞東西時,不再需要 URL encode,好處就是長度短了,以及好看了一點,畢竟 % 有點視覺污染(實際上,還可以直接將編碼后的東西存數據庫了,因為 base64 比 URLEncode 更通用了 )

Base64 的由來——參考 RFC

RFC 向來都不會說明設計的歷史由來,自然 base64 編碼也是一樣,我參考的 rfc4648也只是說明了因為當時開發者們自己發明使用base 64并不規范,沒有統一的標準,因此定義了一份通用標準。

然后呢,Base64 就是自己選了 ASCII 子集(64 個字符)為標準字符集,當然這也是因為 64 是 2的 x 次方 (如 64 就是 2 的6次方),而1個 bit 分別有 0和 1 兩種狀態,6 個 bit也就是 2 的 6 次方=64 個狀態,剛好可以表示 64 個字符,因此 6 個 bit 就可以表達出 64 個字符了。就是下面定義的 64 個:

                      Table 1: The Base 64 Alphabet

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

編碼定義

The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters.

  • 輸入:二進制(圖片,文件,字符串本質就是二進制)
  • 輸出:編碼后的字符串
  • 處理過程:處理輸入的二進制時,每 24 個 bit (3 個字節)作為一組,編碼輸出為 base64 處理后的 4 個標準字符集中的字符。

值得注意的是,網上的示例或說明中,都或多或少有以下偏頗之處:

  • 輸入的例子可以是16 進制數字、二進制、一串數字等,很多文章舉的例子都是字符串;讓人忽略 binary to text 的 binary
  • 是每 24 位(同樣需要注意不一定是 3 個 8 位的字符,3 個字節bytes才準確)為一組來處理,輸出 4 個編碼后的字符。強調這點是因為,24 位為一組,不夠的都需要補 =,如按其他人的文章說的 8 位 8 位的轉,根本不清楚要補多少 =
  • 24 位轉成 4 個編碼后的字符(也就是 4*8=32位),所以編碼后的長度肯定會變大
  • 綜上所述,RFC 原文才是最對的定義,有時細微的區別意味著理解有問題。下面會一一說明。

特殊處理

When fewer than 24 input
bits are available in an input group, bits with value zero are added
(on the right) to form an integral number of 6-bit groups.
Padding at the end of the data is performed using the '=' character.

  • 每 24 位為一組來編碼輸入的 binary 時,如果最后的一組不足24 位,往后補 0直到 補足到 24
  • 對于最后對于全為 0 的一組,補充 =

舉一些例子來說明一下:

 Input data:  0x14fb9c03d97e
      16進制:     1   4    f   b    9   c     | 0   3    d   9    7   e
      2進制:    00010100 11111011 10011100  | 00000011 11011001 01111110
      6位一組:  000101 001111 101110 011100 | 000000 111101 100101 111110
      Decimal: 5      15     46     28       0      61     37     62
      Output:  F      P      u      c        A      9      l      +

16 進制的 0x14fb9c03d97e 作為輸入,先轉成二進制,然后 2 進制的每 24 位 選出來編碼,上面例子就是:00010100 11111011 10011100,然后 6 位一組的分開,得到 000101 001111 101110 011100

然后分別轉 10 進制,也就是 000101 變成 5,001111 變成 15等,再去 base64 定義的字符列表中找出此 10 進制對應的字符,以此類推,就是 base64 后的結果了。

上面例子是輸入剛好是有48 位, 2個 24 位,剛剛夠,不需要補 =

下面看看需要補 = 的例子:

Input data:  0x14fb9c03
      Hex:     1   4    f   b    9   c     | 0   3
      8-bit:   00010100 11111011 10011100  | 00000011 開始補 0 =》00000000 00000000
                                             pad
      6-bit:   000101 001111 101110 011100 | 000000 110000 000000 000000
      Decimal: 5      15     46     28       0      48
                                                  pad with =      =
      Output:  F      P      u      c        A      w      =      =

注意上述輸入只有 32 位,第一個 24 位處理完后,還剩下 8 位,因此需要補16 個 0.

補完后,就是 48 位的輸入了,照樣每 24 位輸出 4 個編碼后的字符。

觀察后半部分,000000 110000 000000 000000,第一個 000000 因為后面還有內容,所以10 進制為 0,因此編碼字符為 A,這個很正常;而 1100000 之后的兩個 6 位 0,都是純粹的填充(pading)了,因此并不用 A 而都用 = 代替掉,注意不用 A

Base64 decode

說完 encode,decode 就容易啦,無非就是逆過程。

一串 base64 后的字符串,根據每個字符在 base64 字符表里找到對應的 10 進制,然后轉成 2 進制,最后多余補足的 000000 去掉。


參考:

https://www.lucidchart.com/techblog/2017/10/23/base64-encoding-a-visual-explanation/

https://www.liaoxuefeng.com/wiki/1016959663602400/1017684507717184
https://www.zhangxinxu.com/wordpress/2018/08/js-base64-atob-btoa-encode-decode/
https://www.wikiwand.com/en/Binary-to-text_encoding
https://www.wikiwand.com/en/Data_URI_scheme
https://tools.ietf.org/html/rfc4648#page-3

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • 1. 什么是Base64 Base64是一種基于64個可打印字符來表示二進制數據的表示方法 Base64是一種編碼...
    理查德成閱讀 2,949評論 0 2
  • 每個文本編輯器都有默認的編碼方式(比如 UTF-8 編碼),當我們保存文檔的時候,可以選擇編碼方式,如果沒有特意選...
    _于曼麗_閱讀 1,547評論 0 1
  • 一個偶然的機會,我在帥sir的朋友圈里看到了彭小六老師的這本書——《讓未來現在就來》。可能是懷著想要改變自己的想法...
    幾個番茄閱讀 468評論 3 6
  • 影子在靜靜發呆 鳥兒在啁啾徘徊 我也在茫茫人海 滾滾紅塵中醒來 任思緒緩緩輕盈 繾綣在眉宇間 烙印在心海 不驚然間...
    大貴貴丿閱讀 331評論 15 15
  • 從zine到印象筆記到簡書。短短兩年里,想法卻空前地多。 “活在當下”。一直所信奉的原則。 不知道是否是正確的事情...
    軒軒的大寶貝閱讀 195評論 0 0