標籤:
1. Base64的由來
Base64最早用於解決電子郵件傳輸問題。由於“曆史問題”,早期的電子郵件網關只允許傳輸ASCII(二進位為00000000-01111111)字元,如果有非ASCII字元經過這種網關時, 字元的二進位位可能會被篡改(如將10000001改為00000001)。由此產生了Base64編碼來保證非ASCII字元的傳輸。
2. 原理
Base64顧名思義是一種基於64個字元的編碼演算法。
如下是Base64的字元對應表,詳情參見RFC 2045
| 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 |
其中的value是10進位的值,Encoding是與之相對應的編碼字元
因為Base64中共有64個字元,所以只需要6個bit就可以表示一個base64字元(*1<<6=64*)。另外,由於電腦基本處理單位為位元組,所以字元編碼是以位元組為單位對字元進行編碼,比如,字元’A’的ASCII編碼為01000001,漢字’你’的UTF-8編碼為11100100 10111101 10100000, GBK編碼為11000100 11100011。由此,Base64的編碼步驟如下:
step1 將待轉碼字串以某種編碼格式(如UTF-8)進行編碼,得到一個位元組數組
step2 將位元組數組按三個位元組為一組進行分組(24個bit)
step3 將每個分組按6 bit進行劃分(不足6位時低位補0),得到N(*2<=N<=4*)個6位二進位碼(以下稱為Base64編碼單元)。
step4 將每個Base64編碼單元轉換為10進位值,並根據上表得到相應的Base64編碼字元,不足4個編碼單元的分組要用=進行填充。
| Base64編碼過程樣本 |
- |
| 原始字串 |
我x |
| UTF-8編碼,分組 |
0xe6 0x88 0x91; 0x78 |
| 二進位表示 |
11100110, 10001000, 10010001; 01111000 |
| 劃分編碼單元 |
111001,101000,100010,010001; 011110,0000000(補位) |
| Value |
57,40,34,17; 30,0 |
| 對應Encoding |
5,o,i,R; e,A, =,=(填充符) |
| 最終結果 |
5oiReA== |
3. 用途
1) 儲存位元據,如密鑰等。
2) 使用Http協議在url中傳輸位元據。
將Base64編碼中不符合URL規定的字元替換成其他合法的字元,並去掉斷行符號換行就得到了UrlBase64編碼。經過UrlBase64編碼,就可以將二進位參數直接放在url中。
這些功能也可以用hex編碼實現,但base64編碼的結果要比hex結果短。
4. 實現
jdk中的類sun.misc.BASE64Encoder和sun.misc.BASE64Decoder提供了Base64的編碼解碼實現,但jdk中sun開頭的包是sun公司的內部實現,該包下的代碼不保證與其他jave平台的相容性(見Why Developers Should Not Write Programs That Call ‘sun’ Packages),所以需要使用第三方實現,也可將這兩個類複製到自己的代碼中。
筆者測試環境中使用的maven依賴如下:
<!--bouncycastle依賴--> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version> </dependency> <!--commons-codec--> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency>
4.1 BouncyCastle實現
包org.bouncycastle.util.encoders下提供了工具類Base64,UrlBase64與Hex用於Base64,UrlBase64與hex編碼/解碼操作。
final String charset="UTF-8";String input="Base64編/解碼";//編碼String encoded=new String(Base64.encode(input.getBytes(charset)),"ASCII");String urlSafeEncoded=new String(UrlBase64.encode(input.getBytes(charset)),"ASCII");//解碼Assert.assertEquals(input,new String(Base64.decode(encoded),charset));Assert.assertEquals(input,new String(UrlBase64.decode(urlSafeEncoded),charset));
4.2 commons-codec實現
工具類org.apache.commons.codec.binary.Base64提供了Base64與UrlBase64的編碼解碼方法。
final String charset="UTF-8";String input="Base64編/解碼";String encoded= Base64.encodeBase64String(input.getBytes(charset));String urlSafeEncoded=Base64.encodeBase64URLSafeString(input.getBytes(charset));Assert.assertEquals(input, new String(Base64.decodeBase64(encoded), charset));Assert.assertEquals(input, new String(Base64.decodeBase64(urlSafeEncoded), charset));
4.3 BouncyCastle與commons-codec區別
標準Base64編碼後的字串每76個字元為一行(稱為分塊),行尾要添加一個斷行符號分行符號“\r\n”。但bouncycastle並沒有這樣做,而commons-codec支援標準實現,但預設並沒有使用。
| ++++++++++++ |
BouncyCastle |
commons-codec |
| 標準(分塊)編碼 |
不支援 |
Base64.encodeBase64Chunked 或 Base64.encodeBase64(binaryData, true) |
| UrlBase64 |
沒有斷行符號換行,‘+’變為’-‘, ’/‘變為’_‘, ’=‘變為’.’ |
與bouncyCastle相同,但去掉了填充符 |
Java加密解密(二) Base64編碼