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 索引表
十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 000000 | A | 16 | 010000 | Q | 32 | 100000 | g | 48 | 110000 | w | |||
1 | 000001 | B | 17 | 010001 | R | 33 | 100001 | h | 49 | 110001 | x | |||
2 | 000010 | C | 18 | 010010 | S | 34 | 100010 | i | 50 | 110010 | y | |||
3 | 000011 | D | 19 | 010011 | T | 35 | 100011 | j | 51 | 110011 | z | |||
4 | 000100 | E | 20 | 010100 | U | 36 | 100100 | k | 52 | 110100 | 0 | |||
5 | 000101 | F | 21 | 010101 | V | 37 | 100101 | l | 53 | 110101 | 1 | |||
6 | 000110 | G | 22 | 010110 | W | 38 | 100110 | m | 54 | 110110 | 2 | |||
7 | 000111 | H | 23 | 010111 | X | 39 | 100111 | n | 55 | 110111 | 3 | |||
8 | 001000 | I | 24 | 011000 | Y | 40 | 101000 | o | 56 | 111000 | 4 | |||
9 | 001001 | J | 25 | 011001 | Z | 41 | 101001 | p | 57 | 111001 | 5 | |||
10 | 001010 | K | 26 | 011010 | a | 42 | 101010 | q | 58 | 111010 | 6 | |||
11 | 001011 | L | 27 | 011011 | b | 43 | 101011 | r | 59 | 111011 | 7 | |||
12 | 001100 | M | 28 | 011100 | c | 44 | 101100 | s | 60 | 111100 | 8 | |||
13 | 001101 | N | 29 | 011101 | d | 45 | 101101 | t | 61 | 111101 | 9 | |||
14 | 001110 | O | 30 | 011110 | e | 46 | 101110 | u | 62 | 111110 | + | |||
15 | 001111 | P | 31 | 011111 | f | 47 | 101111 | v | 63 | 111111 | / | |||
填充 | = |
示例
举例来说,一段引用自托马斯·霍布斯《利维坦》的文句:
经过 Base64 编码之后变成:
编码“Man”的结果为 TWFu,详细原理如下:
文本 | M | a | n | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ASCII 编码 | 77 | 97 | 110 | |||||||||||||||||||||
位元 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
索引 | 19 | 22 | 5 | 46 | ||||||||||||||||||||
Base64 编码 | T | W | F | u |
在此例中,Base64 算法将 3 个字节编码为 4 个字符。
如果要编码的字节数不能被 3 整除,最后会多出 1 个或 2 个字节,那么可以使用下面的方法进行处理:先使用 0 字节值在末尾补足,使其能够被 3 整除,然后再进行 Base64 的编码。在编码后的 Base64 文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余两个八位(待补足)字节(2 个 byte)时,最后一个 6 位的 Base64 字节块有四位是 0 值,最后附加上两个等号;如果最后剩余一个八位(待补足)字节(1 个 byte)时,最后一个 6 位的 base 字节块有两位是 0 值,最后附加一个等号。 参考下表:
文本(1 Byte) | A | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
位元 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
位元(补 0) | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64 编码 | Q | Q | = | = | ||||||||||||||||||||
文本(2 Byte) | B | C | ||||||||||||||||||||||
位元 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
位元(补 0) | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64 编码 | Q | k | M | = |
应用
变种
base64 编码对应的 64 个可打印字符在一些特定的程序或协议中可能不适合使用,特别是字符表第 62 位(+)、63 位(/)和填充字符(=),因此存在一些如下的变种:
编码 | 编码字符 | 换行的处理 | 是否解码非编码字符 | ||||
---|---|---|---|---|---|---|---|
第 62 位 | 第 63 位 | 填充 | 分隔符 | 行字符数 | 校验和 | ||
RFC 1421:用于隐私增强电邮的 Base64(已弃用) | + | / | =强制 | CR+LF | 64,末尾行可少于 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 | 上限 76 | Radix-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 用作编码字符串。