Base64 编码/解码

Base64(基底 64)是一种基于 64 个可打印字符来表示二进制数据的表示方法。由于 log2 64 = 6,所以每 6 个位元为一个单元,对应某个可打印字符。3 个字节相当于 24 个位元,对应于 4 个 Base64 单元,即 3 个字节可由 4 个可打印字符来表示。在 Base64 中的可打印字符包括字母 A-Z、a-z、数字 0-9,这样共有 62 个字符,此外两个可打印符号在不同的系统中而不同。一些如 uuencode 的其他编码方法,和之后 BinHex 的版本使用不同的 64 字符集来代表 6 个二进制数字,但是不被称为 Base64。

Base64 常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括 MIME 的电邮及 XML 的一些复杂数据。

RFC 4648 标准的 Base64 索引表

十进制二进制字符   十进制二进制字符   十进制二进制字符   十进制二进制字符
0000000A16010000Q32100000g48110000w
1000001B17010001R33100001h49110001x
2000010C18010010S34100010i50110010y
3000011D19010011T35100011j51110011z
4000100E20010100U36100100k521101000
5000101F21010101V37100101l531101011
6000110G22010110W38100110m541101102
7000111H23010111X39100111n551101113
8001000I24011000Y40101000o561110004
9001001J25011001Z41101001p571110015
10001010K26011010a42101010q581110106
11001011L27011011b43101011r591110117
12001100M28011100c44101100s601111008
13001101N29011101d45101101t611111019
14001110O30011110e46101110u62111110+
15001111P31011111f47101111v63111111/
填充=

示例

举例来说,一段引用自托马斯·霍布斯《利维坦》的文句:

Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.

经过 Base64 编码之后变成:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

编码“Man”的结果为 TWFu,详细原理如下:

文本Man
ASCII 编码7797110
位元010011010110000101101110
索引1922546
Base64 编码TWFu

在此例中,Base64 算法将 3 个字节编码为 4 个字符。

如果要编码的字节数不能被 3 整除,最后会多出 1 个或 2 个字节,那么可以使用下面的方法进行处理:先使用 0 字节值在末尾补足,使其能够被 3 整除,然后再进行 Base64 的编码。在编码后的 Base64 文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余两个八位(待补足)字节(2 个 byte)时,最后一个 6 位的 Base64 字节块有四位是 0 值,最后附加上两个等号;如果最后剩余一个八位(待补足)字节(1 个 byte)时,最后一个 6 位的 base 字节块有两位是 0 值,最后附加一个等号。 参考下表:

文本(1 Byte)A
位元010000010000000000000000
位元(补 0)010000010000000000000000
Base64 编码QQ==
文本(2 Byte)BC
位元010000100100001100000000
位元(补 0)010000100100001100000000
Base64 编码QkM=

应用

变种

base64 编码对应的 64 个可打印字符在一些特定的程序或协议中可能不适合使用,特别是字符表第 62 位(+)、63 位(/)和填充字符(=),因此存在一些如下的变种:

编码编码字符换行的处理是否解码非编码字符
第 62 位第 63 位填充分隔符行字符数校验和
RFC 1421:用于隐私增强电邮的 Base64(已弃用)+/=强制CR+LF64,末尾行可少于 64
RFC 2045:用于 MIME 的 Base64+/=强制CR+LF上限 76弃用
RFC 2152:用于 UTF-7 的 Base64+/
RFC 3501:用于 IMAP 协议邮箱命名的 Base64+,
RFC 4648§4:标准 base64+/=可选
RFC 4648§5:base64url(URL 和文件名安全标准)-_=可选
RFC 4880:用于 OpenPGP 的 Radix-64+/=强制CR+LF上限 76Radix-64 编码 24 位 CRC

MIME

在 MIME 格式的电邮中,Base64 可以用来将 binary 的字节序列数据编码成 ASCII 字符序列构成的文本。使用时,在传输编码方式中指定 Base64。使用的字符包括大小写拉丁字母各 26 个、数字 10 个、加号+和斜杠/,共 64 个字符,等号=用来作为填充用途。

完整的 Base64 定义可见 RFC 1421 和 RFC 2045。编码后的数据比原始数据略长,为原来的 4/3。在电邮中,根据 RFC 822 规定,每 76 个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的 135.1%。

转换的时候,将 3 字节的数据,先后放入一个 24 位的缓冲区中,先来的字节占高位。数据不足 3 字节的话,于缓冲器中剩下的位元用 0 补足。每次取出 6 位元(因为 26 = 64),按照其值选择 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 中的字符作为编码后的输出,直到全部输入数据转换完成。

若原数据长度不是 3 的倍数时且剩下 1 个输入数据,则在编码结果后加 2 个 =;若剩下 2 个输入数据,则在编码结果后加 1 个 =。

IRCu

在 IRCu 等软件所使用的 P10 IRC 服务器间协议中,对客户与服务器的消息类型号(client/server numerics)和二进制 IP 地址采用了 Base64 编码。消息类型号的长度固定为 3 字节,故可直接编码为 4 个字节而不需要加填充。对 IP 地址进行编码时,则需要在地址前添加一些 0 位元,使之可以编码为整数个字节。这里所用的符号集与前述 MIME 的也有所不同,将 +/ 改成了[]。

UTF-7

UTF-7 是一个修改版 Base64(Modified Base64)。主要是将 UTF-16 的数据,用 Base64 的方法编码为可打印的 ASCII 字符序列。目的是传输 Unicode 数据。主要的区别在于不用等号=补余,因为该字符通常需要大量的转译。

标准可见 RFC 2152,《A Mail-Safe Transformation Format of Unicode》。

URL

Base64 编码可用于在 HTTP 环境下传递较长的标识信息。例如,在 Java 持久化系统 Hibernate 中,就采用了 Base64 来将一个较长的唯一标识符(一般为 128-bit 的 UUID)编码为一个字符串,用作 HTTP 表单和 HTTP GET URL 中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在 URL(包括隐藏表单域)中的形式。此时,采用 Base64 编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。

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

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

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

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

其他

垃圾消息传播者用 Base64 来避过反垃圾邮件工具,因为那些工具通常都不会翻译 Base64 的消息。

在 LDIF 文件,Base64 用作编码字符串。