Base64编码

加密算法

第一节 简介

  Base64不是安全领域下的加密解密算法,只能算是一个编码算法,通常用于把二进制数据编码为可写的字符形式的数据,对数据内容进行编码来适合传输(可以对img图像编码用于传输)。这是一种可逆的编码方式。

  编码后的数据是一个字符串,其中包含的字符为:A-Z、a-z、0-9、+、/,共64个字符(26 + 26 + 10 + 1 + 1 = 64,其实是65个字符,“=”是填充字符。Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。原文的字节最后不够3个的地方用0来补足,转换时Base64编码用=号来代替。这就是为什么有些Base64编码会以一个或两个等号结束的原因,中间是不可能出现等号的,但等号最多只有两个。其实不用”=”也不耽误解码,之所以用”=”,可能是考虑到多段编码后的Base64字符串拼起来也不会引起混淆。)

  Base64编码是从二进制到字符的过程,像一些中文字符用不同的编码转为二进制时,产生的二进制是不一样的,所以最终产生的Base64字符也不一样。例如”上网”对应utf-8格式的Base64编码是”5LiK572R”,对应GB2312格式的Base64编码是”yc/N+A==”。

  标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。

  为解决此问题,可采用一种用于URL的改进Base64编码,它不在末尾填充’=’号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

  另有一种用于正则表达式的改进Base64变种,它将“+”和“/”改成了“!”和“-”,因为“+”,“*”以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。

  此外还有一些变种,它们将“+/”改为“_-”或“.”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“:”(用于XML中的Name)。


第二节 Java中使用Base64编码

2.1 早期

  早期在Java上做Base64的编码与解码,会使用到JDK里sun.misc套件下的BASE64Encoder和BASE64Decoder这两个类别,用法如下:

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws Exception {
final BASE64Encoder encoder = new BASE64Encoder();
final BASE64Decoder decoder = new BASE64Decoder();
final String text ="Java深入";
final byte[] textByte = text.getBytes("UTF-8");
//编码
final String encodedText = encoder.encode(textByte);
System.out.println("编码后:" + encodedText);//编码后:SmF2Yea3seWFpQ==
//解码
System.out.println("解码后:" + new String(decoder.decodeBuffer(encodedText),"UTF-8"));//解码后:Java深入
}

2.2 Apache Commons Codec

  Apache Commons Codec有提供Base64的编码与解码功能,会使用到org.apache.commons.codec.binary套件下的Base64类别,用法如下:

1
2
3
4
5
6
7
final Base64 base64 = new Base64();
final String text = "Java深入";
final byte[] textByte = text.getBytes("UTF-8");
//编码
final String encodedText = base64.encodeToString(textByte);System.out.println(encodedText);
//解码
System.out.println(new String(base64.decode(encodedText),"UTF-8"));

2.3 Java8

  Java 8的java.util套件中,新增了Base64的类别,可以用来处理Base64的编码与解码,用法如下:

1
2
3
4
5
6
7
8
9
final Base64.Decoder decoder = Base64.getDecoder();
final Base64.Encoder encoder = Base64.getEncoder();
final String text = "Java深入";
final byte[] textByte = text.getBytes("UTF-8");
//编码
final String encodedText = encoder.encodeToString(textByte);
System.out.println("编码后:" + encodedText);//编码后:SmF2Yea3seWFpQ==
//解码
System.out.println("解码后:" + new String(decoder.decode(encodedText), "UTF-8"));//解码后:Java深入

  与sun.mis c套件和Apache Commons Codec所提供的Base64编解码器来比较的话,Java 8提供的Base64拥有更好的效能。实际测试编码与解码速度的话,Java 8提供的Base64,要比sun.mis c套件提供的还要快至少11倍,比Apache Commons Codec提供的还要快至少3倍。

2.3.1 三种编码

java.util.Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

  1. Basic编码:标准的BASE64编码,用于处理常规的需求
  2. URL编码:使用下划线替换反斜线“/”有特殊的意义,URL编码会替换掉反斜线
  3. MIME编码:使用基本的字母数字产生BASE64输出,对MIME格式比较适配:每一行输出不超过76个字符,而且每行以“\r\n”符结束。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        //Basic编码
String basicEncoded = Base64.getEncoder().encodeToString("subjects?parm=abcd".getBytes("UTF-8"));
System.out.println("basic encoder: " + basicEncoded);//basic encoder: c3ViamVjdHM/cGFybT1hYmNk
//URL编码
String urlEncoded = Base64.getUrlEncoder().encodeToString("subjects?parm=abcd".getBytes("UTF-8"));
System.out.println("url encoder: " + urlEncoded);//url encoder: c3ViamVjdHM_cGFybT1hYmNk
//MIME编码
StringBuilder sb = new StringBuilder();
for (int t = 0; t < 10; ++t) {
sb.append(UUID.randomUUID().toString());
}
byte[] toEncode = sb.toString().getBytes("UTF-8");
String mimeEncoded = Base64.getMimeEncoder().encodeToString(toEncode);
System.out.println("mime encoder: " + mimeEncoded);

mime encoder: NWZkZjNhYTAtNTM5OS00MWMzLWJkMzctNDQ5MTI2ZTM1MmFlNzgxMmZhYmItODllMS00ZDI5LThi
MTktMzVjNjdiYzQyMzYyMWRmNjdjMmUtNmI4YS00Y2MxLWEwOTAtODdkM2Y2Mzk4OTM3NjYzY2E3
NjEtMThkZi00NTAwLThiNTgtMWZiOTQ5YWUwOTNhN2NhYTIzYWEtMDg1Zi00YTIyLWI5OTAtMGEw
ZGI5YTYzMTRmNzU1OTAwODMtYjg0My00MjQyLTkxZDEtYzgwZGFlZTBmNmIzNzkxM2RmZjEtNGVi
Ny00YTgzLWEyYjYtMGI5NTY5OGE4MjQwYmMwZDE3NDktZTI2Mi00NGNlLWJmOTgtZjZjNWVhNzIw
OTNjMTRjODJhMjUtYWNjNS00ODZhLWIzNmEtMGU4ZTJkYjAzNTFhZDc3OTUzOTItMWQ3NS00ZWZh
LWE4YWItMjFlOWZiNjRkYjMz

2.3.2 流的支持

  java.util.Base64支持流,包括编码和效率都很高,编码器和解码器的输入和输出无需缓冲Buffer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void wrapping() throws IOException {
String src = "This is the content of any resource read from somewhere" +
" into a stream. This can be text, image, video or any other stream.";

// 编码器封装OutputStream, 文件/tmp/buff-base64.txt的内容是BASE64编码的形式
try (OutputStream os = Base64.getEncoder().wrap(new FileOutputStream("/tmp/buff-base64.txt"))) {
os.write(src.getBytes("UTF-8"));
}

// 解码器封装InputStream, 以及以流的方式解码, 无需缓冲
// is being consumed. There is no need to buffer the content of the file just for decoding it.
try (InputStream is = Base64.getDecoder().wrap(new FileInputStream("/tmp/buff-base64.txt"))) {
int len;
byte[] bytes = new byte[100];
while ((len = is.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, len, "UTF-8"));
}
}
}

参考博客和文章书籍等:

Java Base64 编码解码方案总结

因博客主等未标明不可引用,若部分内容涉及侵权请及时告知,我会尽快修改和删除相关内容