Base64最先用于解决电子邮件传输问题。因为“历史问题”,早期的电子邮件网关只容许传输ASCII(二进制为00000000-01111111)字符,若是有非ASCII字符通过这种网关时, 字符的二进制位可能会被篡改(如将10000001改成00000001)。由此产生了Base64编码来保证非ASCII字符的传输。html
Base64顾名思义是一种基于64个字符的编码算法。 以下是Base64的字符映射表,详情参见RFC 2045java
|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是与之相对应的编码字符apache
由于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个编码单元的分组要用=进行填充。数组
例:oracle
Base64编码过程 | - |
---|---|
原始字符串 | 我x |
UTF-8编码,分组 | 0xe6, 0x88, 0x91; 0x78 |
二进制表示 | 11100110, 10001000, 10010001; 01111000 |
划分编码单元 | 111001,101000,100010,010001; 011110,00 00000(补位) |
Value | 57,40,34,17; 30,0 |
对应Encoding | 5,o,i,R; e,A, =,=(填充符) |
最终结果 | 5oiReA== |
jdk中的类sun.misc.BASE64Encoder
和sun.misc.BASE64Decoder
提供了Base64的编码解码实现,但jdk中sun开头的包是sun公司的内部实现,该包下的代码不保证与其余jave平台的兼容性(见Why Developers Should Not Write Programs That Call 'sun' Packages),因此须要使用第三方实现,也可将这两个类复制到本身的代码中。 笔者测试环境中使用的maven依赖以下: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>
包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));
工具类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));
标准Base64编码后的字符串每76个字符为一行(称为分块),行尾要添加一个回车换行符"\r\n"。但bouncycastle并无这样作,而commons-codec支持标准实现,但默认并无使用。编码
++++++++++++ | BouncyCastle | commons-codec |
---|---|---|
标准(分块)编码 | 不支持 | Base64.encodeBase64Chunked 或 Base64.encodeBase64(binaryData, true) |
UrlBase64 | 没有回车换行,'+'变为'-', '/'变为'_', '='变为'.' | 与bouncyCastle相同,但去掉了填充符 |