一文帶你讀懂Base64編碼
本文轉(zhuǎn)載自微信公眾號(hào)「我是開(kāi)發(fā)者FTD」,作者FTD。轉(zhuǎn)載本文請(qǐng)聯(lián)系我是開(kāi)發(fā)者FTD公眾號(hào)。
hi,大家好,我是開(kāi)發(fā)者FTD。相信很多同學(xué)在工作中,經(jīng)常會(huì)用到Base64編碼,那大家知道為什么會(huì)有Base64編碼嗎?我們?yōu)槭裁匆褂盟?,它又是怎么?shí)現(xiàn)的呢?下面就讓我們來(lái)一起深入探究一下Base64編碼吧。
Base 家族
在開(kāi)始之前,我們先給大家介紹一下Base家族。雖然我們?cè)诠ぷ髦惺褂米疃嗟氖荁ase64,但是Base家族可不止是只有Base64,除了Base64之外,Base家族還有Base32和Base16。
我們都知道ASCII 編碼,ASCII 編碼是用256(2的8次方)個(gè)字符,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行編碼的方式,同樣的
- Base64 編碼是用64(2的6次方)個(gè)字符,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行編碼的方式
- Base32 編碼是用32(2的5次方)個(gè)字符,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行編碼的方式
- Base16 編碼是用16(2的4次方)個(gè)字符,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行編碼的方式
那Base家族有這么多編碼形式,為什么偏偏使用Base64呢?
- Base64 編碼是用64(2的6次方)個(gè)特定的ASCII字符來(lái)表示256(2的8次方)個(gè)ASCII字符,也就是說(shuō)三個(gè)ASCII字符經(jīng)過(guò)Base64編碼后變?yōu)樗膫€(gè)的ASCII字符顯示(公約數(shù)為24),編碼后數(shù)據(jù)長(zhǎng)度比原來(lái)增加1/3,不足3n用“=”補(bǔ)足。
- Base32 編碼就是用32(2的5次方)個(gè)特定的ASCII字符來(lái)表示256(2的8次方)個(gè)ASCII碼,也就是說(shuō)五個(gè)ASCII字符經(jīng)過(guò)Base32編碼后會(huì)變?yōu)榘藗€(gè)ASCII字符顯示(公約數(shù)為40),編碼后數(shù)據(jù)長(zhǎng)度比原來(lái)增加3/5,不足8n用“=”補(bǔ)足。
- Base16 編碼就是用16(2的4次方)個(gè)特定的ASCII字符表示256(2的8次方)個(gè)ASCII字符,也就是說(shuō)一個(gè)ASCII字符經(jīng)過(guò)Base16編碼后會(huì)變?yōu)閮蓚€(gè)ASCII字符顯示,編碼后數(shù)據(jù)長(zhǎng)度比原來(lái)增加一倍,不足2n用“=”補(bǔ)足。
從上面可以看出Base64編碼后,長(zhǎng)度增加是最少的,這也是我們選用Base64的一個(gè)重要原因。
Base64 簡(jiǎn)介
Base64顧名思義,就是基于64個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的一種方法,「注意它并不是一種加密算法」。對(duì)于64個(gè)打印字符,我們只需要6個(gè)二進(jìn)制位就可以完全表示了。那么我們?nèi)绾卫?個(gè)二進(jìn)制位來(lái)表示只需要6個(gè)二進(jìn)制位就可以完全表示的可打印字符呢?由于2的6次方等于64,所以我們可以將每6個(gè)位元為一個(gè)單元,對(duì)應(yīng)某個(gè)可打印字符。三個(gè)字節(jié)有24個(gè)位元,對(duì)應(yīng)于4個(gè)Base64單元,即3個(gè)字節(jié)需要用4個(gè)可打印字符來(lái)表示。
Base64是從二進(jìn)制數(shù)據(jù)到字符的過(guò)程。所以計(jì)算機(jī)中所有的內(nèi)容,包括文本、圖片、音頻、視頻等等都可以使用Base64編碼來(lái)表示。
Base64 編碼原理
Base64編碼就是使用64個(gè)字符作為一個(gè)基本字符集:
小寫(xiě)字母a-z、大寫(xiě)字母A-Z、數(shù)字0-9、符號(hào)"+"、"/"(再加上作為墊字的"=",實(shí)際上是65個(gè)字符)
然后,所有其他符號(hào)都根據(jù)一定規(guī)則轉(zhuǎn)換成這個(gè)字符集中的字符。
具體來(lái)說(shuō),Base64編碼的轉(zhuǎn)換方式可以分為以下四步:
- 第一步,將每三個(gè)字節(jié)作為一組,一共是24個(gè)二進(jìn)制位
- 第二步,將這24個(gè)二進(jìn)制位分為四組,每個(gè)組有6個(gè)二進(jìn)制位
- 第三步,在每組前面加兩個(gè)00,擴(kuò)展成32個(gè)二進(jìn)制位,即四個(gè)字節(jié)
- 第四步,根據(jù)下表,得到擴(kuò)展后的每個(gè)字節(jié)的對(duì)應(yīng)符號(hào),這就是Base64的編碼值
Base64 編碼的字符索引表如下所示:
| 數(shù)值 | 字符 | 數(shù)值 | 字符 | 數(shù)值 | 字符 | 數(shù)值 | 字符 |
|---|---|---|---|---|---|---|---|
| 0 | A | 16 | Q | 32 | g | 48 | w |
| 1 | B | 17 | R | 33 | h | 49 | x |
| 2 | C | 18 | S | 34 | i | 50 | y |
| 3 | D | 19 | T | 35 | j | 51 | z |
| 4 | E | 20 | U | 36 | k | 52 | 0 |
| 5 | F | 21 | V | 37 | l | 53 | 1 |
| 6 | G | 22 | W | 38 | m | 54 | 2 |
| 7 | H | 23 | X | 39 | n | 55 | 3 |
| 8 | I | 24 | Y | 40 | o | 56 | 4 |
| 9 | J | 25 | Z | 41 | p | 57 | 5 |
| 10 | K | 26 | a | 42 | q | 58 | 6 |
| 11 | L | 27 | b | 43 | r | 59 | 7 |
| 12 | M | 28 | c | 44 | s | 60 | 8 |
| 13 | N | 29 | d | 45 | t | 61 | 9 |
| 14 | O | 30 | e | 46 | u | 62 | + |
| 15 | P | 31 | f | 47 | v | 63 | / |
有了這個(gè)字符索引表,我們就可以把任意的二進(jìn)制轉(zhuǎn)換成Base64的編碼了,下面我們通過(guò)幾個(gè)例子,給大家展示一下轉(zhuǎn)換的過(guò)程。
1,假設(shè)現(xiàn)在有字符串 「FTD」 需要轉(zhuǎn)換成base64的編碼格式
- 第一步:“F”、“T”、"D" 字符對(duì)應(yīng)的ASCII碼值分別為70,84,68,對(duì)應(yīng)的二進(jìn)制值是01000110、01010100、01000100。如圖第二三行所示,由此組成一個(gè)24位的二進(jìn)制字符串。
- 第二步:將24位二進(jìn)制按照每6位二進(jìn)制位一組分成四組。
- 第三步:在上面每一組前面補(bǔ)兩個(gè)0,擴(kuò)展成32個(gè)二進(jìn)制位,此時(shí)變?yōu)樗膫€(gè)字節(jié):00010001、00100101、00010001、00000100。分別對(duì)應(yīng)的值(Base64編碼索引)為:17、37、17、4。
- 第四步:用上面的值在Base64 字符索引表中進(jìn)行查找,分別對(duì)應(yīng):R、I、R、E。
因此字符串 “FTD” 經(jīng)過(guò)Base64 編碼之后就變?yōu)椋篟IRE 。
2,上面的例子中的字符正好是三個(gè)字節(jié),如果字節(jié)數(shù)不足三個(gè)時(shí)該如何處理呢?下面我們以「F」 和 「FT」 分別舉例說(shuō)明如下:
如上表所示,由于字符F的二進(jìn)制為01000110,按照每6位進(jìn)行分組,此時(shí)只能分成一組,第二組缺少4位,如果位數(shù)不足時(shí),用0補(bǔ)齊;第三組和第四組完全沒(méi)有數(shù)據(jù),則用**=「補(bǔ)上。因此,字符F經(jīng)過(guò)Base64編碼后得到的數(shù)值為」Rg==**。
3,下面我們?cè)倏匆幌氯绻挥袃蓚€(gè)字符的情況:
如上表所示,這個(gè)也屬于位數(shù)不足,需要補(bǔ)位的情況。第一組和第二組按照正常的分組計(jì)算,第三組由于不足位數(shù),最后兩位補(bǔ)0,第四組完全沒(méi)有數(shù)據(jù),用**=「補(bǔ)上。因此,字符FT經(jīng)過(guò)Base64編碼后得到的數(shù)值為」RlQ=**。
關(guān)于中文的Base64編碼
大家都知道中文編碼有很多種,例如「GB2312、GBK、GB18030」,不同的漢字使用不同的編碼格式進(jìn)行編碼后,它的二進(jìn)制是不同的,所以在進(jìn)行Base64編碼后,他們的Base64編碼的值也是不同的。這就要求我們?cè)诮獯a的時(shí)候需要注意原文的字符集格式,一定要保持一致才能正確解碼。
例如:
中文 “【我是開(kāi)發(fā)者FTD】公眾號(hào)” UTF-8 格式的Base64 編碼后的值是:44CQ5oiR5piv5byA5Y+R6ICFRlRE44CR5YWs5LyX5Y+3
中文 “【我是開(kāi)發(fā)者FTD】公眾號(hào)” GB2312 格式的Base64 編碼后的值是:ob7O0srHv6q3otXfRlREob+5q9bausU=
Base64 是加密算法嗎?
Base64 主要不是用來(lái)加密的,它主要的用途是把一些二進(jìn)制數(shù)轉(zhuǎn)成普通字符用于網(wǎng)絡(luò)傳輸,這是因?yàn)橐恍┒M(jìn)制字符在傳輸協(xié)議中屬于控制字符,不能直接在網(wǎng)絡(luò)上傳輸。另外,還有一些系統(tǒng)中只能使用ASCII字符。Base64 編碼就是用來(lái)將非ASCII字符的數(shù)據(jù)轉(zhuǎn)換成ASCII字符的一種方法。Base64 并不是安全領(lǐng)域下的加密解密算法,雖然有時(shí)候也會(huì)經(jīng)??吹剿^的Base64加密解密算法。其實(shí)Base64只能算是一個(gè)編碼算法,對(duì)數(shù)據(jù)內(nèi)容進(jìn)行編碼來(lái)適合網(wǎng)絡(luò)傳輸。雖然Base64編碼過(guò)后原文也變成無(wú)法直接理解的字符格式,但是這種編碼方式比較初級(jí),很簡(jiǎn)單,很容易就可以被還原成原文,所以如果有比較重要的信息需要加密,一定要使用我們之前文章中介紹的那些加密算法進(jìn)行數(shù)據(jù)的安全保護(hù)。
Base64 編碼實(shí)現(xiàn)
Java語(yǔ)言中有多個(gè)庫(kù)實(shí)現(xiàn)了Base64編碼,不管哪一個(gè)庫(kù),最終的結(jié)果都是一樣的。
JDK 提供的 Base64 編碼實(shí)現(xiàn):
- public static String encode(String data) {
- return Base64.getEncoder().encodeToString(data.getBytes());
- }
- public static String decode(String base64Data) {
- return new String(Base64.getDecoder().decode(base64Data));
- }
Bouncy Castle 提供的 Base64 編碼實(shí)現(xiàn):
- public static String encode(String data) {
- return new String(Base64.encode(data.getBytes()));
- }
- public static String decode(String base64Data) {
- return new String(Base64.decode(base64Data));
- }
Commons Codec 提供的 Base64 編碼實(shí)現(xiàn):
- public static String encode(String data) {
- return Base64.encodeBase64String(data.getBytes());
- }
- public static String decode(String base64Data) {
- return new String(Base64.decodeBase64(base64Data));
- }
下面讓我們用Java語(yǔ)言的實(shí)現(xiàn)來(lái)驗(yàn)證一下,我們第二章節(jié)的推理是否正確吧,代碼如下:
- public static void main(String[] args) {
- String ftd = "FTD";
- String ft = "FT";
- String f = "F";
- System.out.println("FTD base64 編碼:" + encode(ftd));
- System.out.println("FT base64 編碼:" + encode(ft));
- System.out.println("F base64 編碼:" + encode(f));
- }
輸出結(jié)果為:
- FTD base64 編碼:RlRE
- FT base64 編碼:RlQ=
- F base64 編碼:Rg==
可以看到,和我們分析所得的結(jié)果是完全一樣的。
查看完整代碼請(qǐng)?jiān)L問(wèn):
https://github.com/ForTheDevelopers/JavaSecurity
總結(jié)
Base64是我們?cè)诠ぷ髦薪?jīng)常用到,但是很少有人會(huì)深入研究一下它的實(shí)現(xiàn)原理,如果理解不當(dāng),甚至可能還會(huì)有人用它當(dāng)做加解密用到業(yè)務(wù)系統(tǒng)關(guān)鍵位置,可能會(huì)引發(fā)比較嚴(yán)重的后果,相信大家看完上述的內(nèi)容后,應(yīng)該對(duì)Base64編碼已經(jīng)有了深刻的理解了吧。

































