【安全】Base64編碼
Base64編碼原理
base64編碼就是用64個(gè)可打印的字符作為一個(gè)字符集來(lái)表示你的數(shù)據(jù)。這64個(gè)字符包括A-Z、a-z、0-9這62個(gè)常見字符,另外還有兩個(gè)可打印字符在不同的系統(tǒng)中可能會(huì)有所不同,我們常見的是+和/這兩個(gè)字符,如下:

Base64編碼本質(zhì)上是一種將二進(jìn)制數(shù)據(jù)轉(zhuǎn)成文本數(shù)據(jù)的方案。對(duì)于非二進(jìn)制數(shù)據(jù),是先將其轉(zhuǎn)換成二進(jìn)制形式,然后每連續(xù)6比特(2的6次方=64)計(jì)算其十進(jìn)制值,根據(jù)該值在上面的索引表中找到對(duì)應(yīng)的字符,最終得到一個(gè)文本字符串。
假設(shè)我們要對(duì)?Hello! 進(jìn)行Base64編碼,按照ASCII表,其轉(zhuǎn)換過(guò)程如下圖所示:

可知 Hello! 的Base64編碼結(jié)果為 SGVsbG8h ,原始字符串長(zhǎng)度為6個(gè)字符,編碼后長(zhǎng)度為8個(gè)字符,每3個(gè)原始字符經(jīng)Base64編碼成4個(gè)字符。
但這里需要注意一個(gè)點(diǎn):Base64編碼是每3個(gè)原始字符編碼成4個(gè)字符,如果原始字符串長(zhǎng)度不能被3整除,那怎么辦?使用0值來(lái)補(bǔ)充原始字符串。以?Hello!!?為例,其轉(zhuǎn)換過(guò)程為:

注:圖表中藍(lán)色背景的二進(jìn)制0值是額外補(bǔ)充的。
Hello!! Base64編碼的結(jié)果為 SGVsbG8hIQAA 。最后2個(gè)零值只是為了Base64編碼而補(bǔ)充的,在原始字符中并沒有對(duì)應(yīng)的字符,那么Base64編碼結(jié)果中的最后兩個(gè)字符 AA 實(shí)際不帶有效信息,所以需要特殊處理,以免解碼錯(cuò)誤。
標(biāo)準(zhǔn)Base64編碼通常用 = 字符來(lái)替換最后的 A,即編碼結(jié)果為 SGVsbG8hIQ==。因?yàn)?= 字符并不在Base64編碼索引表中,其意義在于結(jié)束符號(hào),在Base64解碼時(shí)遇到 = 時(shí)即可知道一個(gè)Base64編碼字符串結(jié)束。
如果Base64編碼字符串不會(huì)相互拼接再傳輸,那么最后的 = 也可以省略,解碼時(shí)如果發(fā)現(xiàn)Base64編碼字符串長(zhǎng)度不能被4整除,則先補(bǔ)充 = 字符,再解碼即可。
解碼是對(duì)編碼的逆向操作,但注意一點(diǎn):對(duì)于最后的兩個(gè) = 字符,轉(zhuǎn)換成兩個(gè) A 字符,再轉(zhuǎn)成對(duì)應(yīng)的兩個(gè)6比特二進(jìn)制0值,接著轉(zhuǎn)成原始字符之前,需要將最后的兩個(gè)6比特二進(jìn)制0值丟棄,因?yàn)樗鼈儗?shí)際上不攜帶有效信息。
base64編碼有什么用
郵件傳送
以前發(fā)送郵件只支持可見字符的傳送。由此,需要有一個(gè)方法將不可見的字符轉(zhuǎn)換為可見的字符,Base64編碼算法便可解決這個(gè)問題。
圖片的base64編碼
圖片的base64編碼主要是為了用在網(wǎng)頁(yè)上減少請(qǐng)求次數(shù),base64編碼可以將圖片添加到css中,實(shí)現(xiàn)請(qǐng)求css即可下載下來(lái)圖片,減少了再次請(qǐng)求圖片的請(qǐng)求。
URL Base64算法
Base64編碼值通過(guò)URL傳輸會(huì)出現(xiàn)問題,因?yàn)锽ase64編碼中的“+”和“/”符號(hào)是不允許出現(xiàn)在URL中的。同樣,符號(hào)“=”用做參數(shù)分隔符,也不允許出現(xiàn)在URL中;為了解決這些問題,有一種URL安全的Base64編碼,URL安全的Base64編碼特點(diǎn):
(1) 不能被3整除時(shí),不補(bǔ)=符號(hào)。
(2) 生成Base64編碼中,"+"和"/"被替換成其他非URL保留字符,使其可以直接放入U(xiǎn)RL中傳輸。比如"+"和"/"被替換成"-"和"_"。
java Base64庫(kù)
標(biāo)準(zhǔn)Base64編解碼
JDK8開始已經(jīng)提供了Base64的實(shí)現(xiàn)
我們以?Hello!!?為例來(lái)驗(yàn)證其編碼結(jié)果是不是前面所說(shuō)的SGVsbG8hIQ==
import?java.util.Base64;
class?Base64Test{
????//標(biāo)準(zhǔn)Base64編解碼
????public?void?test0(){
????????//編碼
????????System.out.println(Base64.getEncoder().encodeToString("Hello!!".getBytes()));
????????//解碼
????????byte[]?bytes?=?Base64.getDecoder().decode("SGVsbG8hIQ==");
????????for?(byte?b?:?bytes){
????????????System.out.print((char)b);
????????}
????}
}
public?class?Base64Example?{
????public?static?void?main(String[]?args)?{
????????Base64Test?base64Test?=?new?Base64Test();
????????base64Test.test0();
????}
}
******************【運(yùn)行結(jié)果】******************
SGVsbG8hIQ==
Hello!!可見程序輸出也是:SGVsbG8hIQ==
URL Base64算法
要使用URL Base64算法,需要引入common codec依賴包:
commons-codec
commons-codec
1.14
package?Base64;
import?java.util.Base64;
class?Base64Test{
????//URL?Base64算法
????public?void?test1(){
????????System.out.println();
????????String?value?=?"Hello!!";
????????System.out.println(Base64.getEncoder().encodeToString(value.getBytes()));
????????System.out.println(org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(value.getBytes()));
????}
}
public?class?Base64Example?{
????public?static?void?main(String[]?args)?{
????????Base64Test?base64Test?=?new?Base64Test();
????????base64Test.test1();
????}
}
******************【運(yùn)行結(jié)果】******************
SGVsbG8hIQ==
SGVsbG8hIQ通過(guò)對(duì)比,確實(shí)相對(duì)于標(biāo)準(zhǔn)的Base64算法,URL Base64算法編碼的結(jié)果沒有=填充符了。
本文源碼地址:
https://github.com/qinlizhong1/javaStudy/tree/master/javaExample/src/main/java/Base64
本文示例代碼環(huán)境:
操作系統(tǒng):macOs 12.1
JDK版本:12.0.1
??掃描上方二維碼關(guān)注
