64 个字符
Base64 每次读取 3 个字节(24 位)的输入,把它们拆成四个 6 位的数。每个数的范围是 0 到 63,用作这张表的索引。顺序由 RFC 4648 规定:0 到 25 是大写字母,26 到 51 是小写字母,52 到 61 是数字,然后是 + 和 /。最后一列是该字符的 ASCII 码,也就是实际存储或传输的内容。
| 索引 | 6 位 | Base64 | Base64URL | ASCII 码 |
|---|---|---|---|---|
| 0 | 000000 | A | A | 0x41 |
| 1 | 000001 | B | B | 0x42 |
| 2 | 000010 | C | C | 0x43 |
| 3 | 000011 | D | D | 0x44 |
| 4 | 000100 | E | E | 0x45 |
| 5 | 000101 | F | F | 0x46 |
| 6 | 000110 | G | G | 0x47 |
| 7 | 000111 | H | H | 0x48 |
| 8 | 001000 | I | I | 0x49 |
| 9 | 001001 | J | J | 0x4A |
| 10 | 001010 | K | K | 0x4B |
| 11 | 001011 | L | L | 0x4C |
| 12 | 001100 | M | M | 0x4D |
| 13 | 001101 | N | N | 0x4E |
| 14 | 001110 | O | O | 0x4F |
| 15 | 001111 | P | P | 0x50 |
| 16 | 010000 | Q | Q | 0x51 |
| 17 | 010001 | R | R | 0x52 |
| 18 | 010010 | S | S | 0x53 |
| 19 | 010011 | T | T | 0x54 |
| 20 | 010100 | U | U | 0x55 |
| 21 | 010101 | V | V | 0x56 |
| 22 | 010110 | W | W | 0x57 |
| 23 | 010111 | X | X | 0x58 |
| 24 | 011000 | Y | Y | 0x59 |
| 25 | 011001 | Z | Z | 0x5A |
| 26 | 011010 | a | a | 0x61 |
| 27 | 011011 | b | b | 0x62 |
| 28 | 011100 | c | c | 0x63 |
| 29 | 011101 | d | d | 0x64 |
| 30 | 011110 | e | e | 0x65 |
| 31 | 011111 | f | f | 0x66 |
| 32 | 100000 | g | g | 0x67 |
| 33 | 100001 | h | h | 0x68 |
| 34 | 100010 | i | i | 0x69 |
| 35 | 100011 | j | j | 0x6A |
| 36 | 100100 | k | k | 0x6B |
| 37 | 100101 | l | l | 0x6C |
| 38 | 100110 | m | m | 0x6D |
| 39 | 100111 | n | n | 0x6E |
| 40 | 101000 | o | o | 0x6F |
| 41 | 101001 | p | p | 0x70 |
| 42 | 101010 | q | q | 0x71 |
| 43 | 101011 | r | r | 0x72 |
| 44 | 101100 | s | s | 0x73 |
| 45 | 101101 | t | t | 0x74 |
| 46 | 101110 | u | u | 0x75 |
| 47 | 101111 | v | v | 0x76 |
| 48 | 110000 | w | w | 0x77 |
| 49 | 110001 | x | x | 0x78 |
| 50 | 110010 | y | y | 0x79 |
| 51 | 110011 | z | z | 0x7A |
| 52 | 110100 | 0 | 0 | 0x30 |
| 53 | 110101 | 1 | 1 | 0x31 |
| 54 | 110110 | 2 | 2 | 0x32 |
| 55 | 110111 | 3 | 3 | 0x33 |
| 56 | 111000 | 4 | 4 | 0x34 |
| 57 | 111001 | 5 | 5 | 0x35 |
| 58 | 111010 | 6 | 6 | 0x36 |
| 59 | 111011 | 7 | 7 | 0x37 |
| 60 | 111100 | 8 | 8 | 0x38 |
| 61 | 111101 | 9 | 9 | 0x39 |
| 62 | 111110 | + | - | 0x2B |
| 63 | 111111 | / | _ | 0x2F |
| 填充 | – | = | 通常省略 | 0x3D |
有两个区间值得记住。从 A 到 P(0 到 15)的每个字符都以比特 00 开头,从 w 到 /(48 到 63)的每个字符都以 11 开头。手工检查前缀时,靠这两条就能在脑子里解出一个字符。比特拆分的完整示例见 Base64 的工作原理。
字母表的顺序不是 ASCII 顺序。在 ASCII 中,+ 和 / 排在最前,然后是数字、大写字母、小写字母。因此对 Base64 字符串排序,并不等于对底层字节排序:+A==(字节 F8)会排在 AA==(字节 00)前面。如果需要编码后的值与原始字节排序一致,请使用 RFC 4648 中的 base32hex 或直接用 hex。
URL 安全字母表
+ 和 / 在 URL 中都有特殊含义。在查询字符串和表单数据里,+ 会被解码成空格,而 / 用来分隔路径段。RFC 4648 第 5 节定义的 Base64URL 保留索引 0 到 61 不变,只替换最后两个:
| 索引 | Base64 | Base64URL |
|---|---|---|
| 62 | + | - |
| 63 | / | _ |
其余部分(包括比特布局)完全相同,所以两者互转只需替换字符,再补上或去掉填充。JWT、许多 API 令牌以及可安全用作文件名的标识符都使用 Base64URL,而且通常不带 =。Base64URL 编码器 可以直接生成这种格式。完整对比见 Base64 与 Base64URL 的区别。
填充符
=(ASCII 0x3D)不属于这 64 个字符,也从不编码数据。只有当输入长度不是 3 的倍数时,它才用来填满最后一个 4 字符组:
- 剩 1 个字节:2 个数据字符加
==。 - 剩 2 个字节:3 个数据字符加
=。 - 没有剩余字节:不填充。
所以带填充的字符串长度总是 4 的倍数,你也永远不会看到 ===。不带填充时,长度除以 4 的余数是 0、2 或 3,绝不会是 1。何时可以省略 =、如何把它补回来,见 填充指南。
哪些字符出现在哪些位置
有几条规律直接来自比特布局。它们都经过对所有相关字节值的穷举验证。
== 前面只会出现 A、Q、g 或 w。剩 1 个字节时,第二个字符包含 2 个数据位,后面跟 4 个零位,所以它的索引只能是 0、16、32 或 48。单个 = 前面的第三个字符末尾有 2 个零位,因此只能是这 16 个字符之一:A E I M Q U Y c g k o s w 0 4 8。这个位置出现其他字符,说明编码不规范,严格的解码器会拒绝它。
随机字节会均匀地用到全部 64 个字符。随机数据的每个 6 位组都是均匀分布的,所以加密数据、密钥和压缩文件的字符分布是平的。分布明显不均,说明数据是文本或者有某种结构。
ASCII 文本的组首字符不会是小写 g 到 z、数字、+ 或 /。每个 4 字符组的第一个字符承载某个字节的高 6 位。7 位 ASCII 的最高位是 0,所以这个字符总在 A 到 f 之间(索引 0 到 31)。在 SGVsbG8gd29ybGQh(Hello world!)中,各组的首字符分别是 S、b、d 和 b。
ASCII 文本的 Base64 里很少有 + 和 /。对于小于 0x80 的字节,+ 只能作为某组的第四个字符出现,且第三个字节是 > 或 ~。/ 只有在该字节是 ? 或 DEL 控制字符时才会出现在那里。这就是 Hi?> 编码为 SGk/Pg==、<b> 编码为 PGI+ 的原因。
连续的 A 是零字节,连续的 / 是 0xFF。AAAA 是三个 00 字节,//// 是三个 FF 字节。大段的 A 常见于稀疏的二进制格式、UTF-16 文本(Hello 是 SABlAGwAbABvAA==)以及 SSH 密钥开头的长度字段。更多可辨认的模式见 常见的 Base64 前缀。
其他 Base64 字母表
有几个系统沿用了“每 6 位对应 64 个字符之一”的思路,但选用了不同的字符:
| 变体 | 按索引顺序排列的字母表 | 使用场景 |
|---|---|---|
| 标准 | A-Z a-z 0-9 + / | RFC 4648、MIME、PEM、大多数 API |
| URL 安全 | A-Z a-z 0-9 - _ | RFC 4648 第 5 节、JWT |
| IMAP 邮箱名 | A-Z a-z 0-9 + , | RFC 3501 修改版 UTF-7 |
| bcrypt | . / A-Z a-z 0-9 | $2a$、$2b$、$2y$ 密码哈希 |
| crypt(3) | . / 0-9 A-Z a-z | MD5-crypt $1$、SHA-crypt $5$ 和 $6$、DES crypt |
| xxencode | + - 0-9 A-Z a-z | 早期 Unix 邮件和 Usenet |
| uuencode | ASCII 32 到 95(空格到 _),0 常用 ` 表示 | 早期 Unix 邮件和 Usenet |
- IMAP 把
/换成了,,因为/是邮箱名中常用的层级分隔符。名为Entwürfe的文件夹会存储为Entw&APw-rfe。 - bcrypt 把
.和/移到最前面,并让字母排在数字前面。它的盐值和哈希都是用这个字母表、不带填充的 Base64,所以用标准解码器解出来的字节是错的。 - crypt(3) 使用
./0-9A-Za-z,这正好是 ASCII 顺序。它还从最低有效位开始打包比特,所以即使换算了字母表,标准解码器也还原不出哈希字节。 - uuencode 和 xxencode 比 MIME 还早。uuencode 和 xxencode 工具都能读写这两种格式。
对于字母表大小不同的编码,例如 Base32、Base58 和 Base85,请参阅 Base64 与 hex、Base32、Base58、Base85 的对比。本文用到的术语定义见 术语表。
常见问题
Base64 字母表一共有多少个字符?
用于承载数据的有 64 个:26 个大写字母、26 个小写字母、10 个数字,加上 + 和 /。填充符 = 是第 65 个符号,它从不承载数据。
字母 A 在 Base64 中对应的值是多少?
A 代表索引 0,二进制为 000000。B 是 1,Z 是 25,a 是 26,z 是 51,0 是 52,9 是 61,+ 是 62,/ 是 63。
Base64URL 改了哪些字符?
只改了两个:+(索引 62)变成 -,/(索引 63)变成 _。通常还会去掉填充。可以用 Base64 转 Base64URL 转换。