加密就是為了安全通信而誕生的擅耽。沒有通信打毛,加密也沒有太大存在的意義司恳。
雖說Base64算不上一種加密途乃,只是一種具有固定標準的編碼方式,但如果適當?shù)淖儞Q一下編碼字典扔傅,不采用標準的編碼方式耍共,那對于不知道編碼字典的第三方來說就是一種加密方式烫饼。
用途
在介紹Base64編碼之前,我想先介紹下Base64的實際應用:
1)在前端中试读,當網(wǎng)頁上有數(shù)目龐大的圖片或其他資源時杠纵,瀏覽器會建立大量的http請求去獲取資源。這樣對服務器來說會產(chǎn)生大量的請求钩骇,浪費服務器性能比藻。對于客戶端來說,會多次請求資源倘屹,網(wǎng)絡利用率不高银亲,用戶體驗不好。所以纽匙,前端上有一個解決辦法就是群凶,對于大量的較小圖片采用雪碧圖(Sprite),一次將一張較大圖片加載回來,然后進行選擇展示哄辣。但是這樣會在js请梢、css、html代碼中增加很多附加代碼力穗,不利于后期操作與維護毅弧。于是,Base64的優(yōu)勢便顯現(xiàn)出來当窗,可以將圖片進行Base64編碼后嵌入到網(wǎng)頁中够坐,在展示時在將其進行Base64解碼,顯示在網(wǎng)頁上崖面。舉一個具體的實例:在google的首頁搜索框的搜索小圖標就是在用這樣的方式實現(xiàn)元咙。
2)迅雷的種子:),大家一定懂得巫员。為了不直接使下載鏈接暴露出來庶香,迅雷(旋風等下載工具)都會對鏈接進行Base64編碼。因為Base64編解碼規(guī)則是公開的简识,所以你只需要對迅雷的種子進行Base64解碼之后拿到下載鏈接赶掖,在別的地方下載,但是迅雷不會這樣單純的七扰。一般來說迅雷會對編碼表進行定制奢赂,在對編碼方式進行一個修改,這樣不知道規(guī)則的人就無法解密了颈走。
3)在只能發(fā)送字符的通信方式進行文件傳輸膳灶。文件是由一系列二進制數(shù)據(jù)組成,而字符集的編碼數(shù)量是小于byte的編碼數(shù)量的(ASCII:0127立由、BYTE:0255)轧钓。所以通過只能發(fā)字符的通信方式進行文件的傳輸看上去有些不可能司致,但是Base64恰恰解決的這個問題,Base64可以將byte映射到64個可見字符上聋迎。使用Base64將文件進行編碼脂矫,形成一個字符串,在將字符串傳給對方霉晕,對方收到后在進行解碼庭再,就可以還原出這個文件。
4)簡單的加密通信牺堰。本文開始時拄轻,提到Base64使用非標準的字典進行編碼時,對于第三方來說就是一種加密方式伟葫,對于第三方來說恨搓,想要破解,只能通過概率論來將編碼字典得出筏养。對于一些對加密效率要求高斧抱,而不要求太高的安全性的應用來說是一個比較好的選擇。
Base64是什么
Base64是基于64個可見字符的編碼方式渐溶,從Base64這個名字上可以得出辉浦。RCF文檔中對標準Base64的編碼字典規(guī)定為:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
‘=’最為填充字符
可以將任意二進制數(shù)映射到這64個字符中。
Base64的原理
舉一個實際的例子來說明:
情況一
對二進制 0x12
茎辐、0x34
宪郊、0x56
,進行Base64編碼拖陆。
1)00010010
弛槐、00110100
、01010110
依啰,每六個一組分為:000100
乎串、100011
、010001
孔飒、010110
灌闺。
2)在計算機看來艰争,上述新分組后的值為:00000100
坏瞄、00100011
、00010001
甩卓、000101110
鸠匀。相當于對每組數(shù)的高兩位進行了零填充,也就是說我們分完組之后的數(shù)所能表示的最大的范圍就是2^6 : 0 ~ 63逾柿,共64個數(shù)缀棍。
- 按照一一對應的方式宅此,在編碼字典中選取響應位置上的字符,分別是第
12
爬范、33
父腕、17
、46
位青瀑,查編碼字典為:M
璧亮、h
、R
斥难、u
枝嘶。
4)編碼后為:"MhRu"
最理想的情況是,待編碼的字節(jié)數(shù)是3的倍數(shù)哑诊,編碼后的字節(jié)數(shù)是4的倍數(shù)群扶,而上述展示的就是最理想的情況。
情況二
下面展示除3余1的情況:
對二進制0x12
镀裤、0x34
竞阐、0x56
、0x78
暑劝,進行Base64編碼馁菜。
1)00010010
、00110100
铃岔、01010110
汪疮、01111000
,每六個一組分為:000100
毁习、100011
智嚷、010001
、010110
纺且、011110
盏道、000000
- 在計算機看來,上述新分組后的值為:
00000100
载碌、00100011
猜嘱、00010001
、000101110
嫁艇、00011110
朗伶、00000000
。 - 按照一一對應的方式步咪,在編碼字典中選取響應位置上的字符论皆,分別是第
12
、33
、17
点晴、46
感凤、0
位,查編碼字典為:M
粒督、h
陪竿、R
、u
屠橄、e
萨惑、A
- 為了滿足編碼后字節(jié)數(shù)為4的倍數(shù),需要在編碼后的字符后面添加'='填充符仇矾,
M
庸蔼、h
、R
贮匕、u
姐仅、e
、A
刻盐、=
掏膏、=
- 編碼后為:
"MhRueA=="
情況三
下面展示除3余2的情況:
對二進制0x12
、0x34
敦锌、0x56
馒疹、0x78
,0x9a
進行Base64編碼乙墙。
-
00010010
颖变、00110100
、01010110
听想、01111000
腥刹、10011010
,每六個一組分為:000100
汉买、100011
衔峰、010001
、010110
蛙粘、011110
垫卤、001001
、101000
- 在計算機看來出牧,上述新分組后的值為:
00000100
穴肘、00100011
、00010001
崔列、000101110
梢褐、00011110
旺遮、00001001
赵讯、00101000
盈咳。 - 按照一一對應的方式,在編碼字典中選取響應位置上的字符边翼,分別是第
12
鱼响、33
、17
组底、46
丈积、30
、9
债鸡、40
位江滨,查編碼字典為:M
、h
厌均、R
唬滑、u
、e
棺弊、J
晶密、o
4)為了滿足編碼后字節(jié)數(shù)為4的倍數(shù),需要在編碼后的字符后面添加'='填充符模她,M
稻艰、h
、R
侈净、u
尊勿、e
、J
畜侦、o
运怖、=
- 編碼后為:
"MhRueJo="
而解碼就是編碼的逆過程
總結(jié)
可以看出,編碼前和編碼后的空間占用比在文件很大時為3:4的關系夏伊,小文件的占用比例會略大摇展。所以,Base64編碼的缺點就是空間占用比增大溺忧,消耗了cpu的資源進行編碼咏连。由于Base64編碼使用的都是可打印的普通字符,所以就能極大的減少在傳輸轉(zhuǎn)換中的錯誤率鲁森。
代碼實現(xiàn)
public class HFBase64 {
//編碼表祟滴,可以自定義,這樣只要雙方都知道編碼表歌溉,就變成一種加密方式了
private static String encodingTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
public HFBase64() {
}
//編碼方法
public String enCode(byte[] datas) {
if (datas == null) {
return null;
}
String ciphertext = "";
Integer byteIndex = 0;
while (byteIndex < datas.length) {
Integer index = 0;
byte[] temByte = new byte[3];
byte temp;
while (index < 3 && byteIndex < datas.length)
temByte[index++] = datas[byteIndex++];
ciphertext += encodingTable.charAt((temByte[0] & 0xfc) >> 2);
ciphertext += encodingTable.charAt(((temByte[0] & 0x03) << 4) | ((temByte[1] & 0xf0) >> 4));
if (index > 1) {
ciphertext += encodingTable.charAt(((temByte[1] & 0x0f) << 2) | (temByte[2] & 0xc0) >> 6);
} else {
ciphertext += '=';
}
if (index > 2) {
ciphertext += encodingTable.charAt((temByte[2] & 0x3f));
} else {
ciphertext += '=';
}
}
return ciphertext;
}
//解碼方法
public byte[] deCode(String datas) {
if (datas == null) {
return null;
}
Integer len = (datas.length()) / 4 * 3;
byte[] plainBytes = new byte[len];
Integer charCount = 0;
Integer index = 0;
while (charCount < datas.length()) {
plainBytes[index++] = (byte) ((this.convertByte(datas.charAt(charCount)) << 2) | (this.convertByte(datas.charAt(++charCount))) >> 4);
if (datas.charAt(charCount + 1) == '=') {
// plainBytes[--index] = (byte) (this.convertByte(datas.charAt(charCount)) >> 4);
break;
}
plainBytes[index++] = (byte) ((this.convertByte(datas.charAt(charCount)) << 4) | (this.convertByte(datas.charAt(++charCount))) >> 2);
if (datas.charAt(charCount + 1) == '=') {
// plainBytes[--index] = (byte) (this.convertByte(datas.charAt(charCount)) >> 2);
break;
}
plainBytes[index++] = (byte) ((this.convertByte(datas.charAt(charCount)) << 6) | (this.convertByte(datas.charAt(++charCount))));
charCount++;
}
byte[] newBytes = new byte[index];
for (int i = 0; i < index; i++) {
newBytes[i] = plainBytes[i];
}
return newBytes;
}
private byte convertByte(char EncodeChar) {
byte index = (byte) encodingTable.indexOf(EncodeChar);
return index;
}
}