简体中文

开发者工具 · Base64 编码器和解码器

Base64 简史:从 uuencode 和 PEM 到今天的字母表

· 背景

base64 编码

从 uuencode 和 PEM 通过 MIME 到 RFC 的时间线 4648 Base64 演变
原始 ToolAcre 矢量图

Base64 的字母表是 1980 年代运输问题的化石记录。这篇文章遵循从 uuencode 通过隐私增强邮件到 MIME 和 RFC 4648 的沿袭,并解释了每个设计选择。

为什么字母表不只是按某种明显顺序排列的 0–63 — 这个问题可以追溯到四十年前

Base64 并未完全形成标准。字母表(A-Z、a-z、0-9、+、/) 是数十年编码实验的化石记录,每个实验都试图解决相同的问题:如何将二进制数据表示为 1970 年代和 1980 年代电子邮件、USENET 和 Unix 工具中幸存的文本。故事涵盖 Unix 上的 uuencode、隐私增强邮件 (RFC) 1993 中的 1421),1996 中的 MIME (RFC 2045),最后是 2006 中的 RFC 4648,合并了所有变体。

了解这段历史可以解释为什么某些字符出现在字母表中以及为什么 RFC 为实现者留下了某些选择。 uuencode 是 Unix-to-Unix 编码的缩写,是第一个解决 Unix 上 7 位传输问题的工具。它在 1980 中创建,将每个 3 字节(24 位)编码为 64 字符字母表中的 4 字符。 uuencode 字母表是 ASCII 32(空格)到 ASCII 95(下划线和其他标点符号),之所以选择这些字符是因为这些字符可以在任何终端上打印。该存储库展示了当前实施的字母表,但它不包含有关谁选择该顺序或每个角色为何获胜的档案证据。因此,标题被缩小:可以准确地检查当前的布局,而动机和日期需要此处未包含的主要历史文献。

为什么字母表看起来很历史——这个存储库没有记录的边界

然而,空格作为编码字符是有问题的:文本编辑器和邮件系统会修剪尾随空格,从而损坏输出。字母表并不理想,但对于 Unix 到 Unix 文件传输来说它已经足够好了。隐私增强邮件(RFC 1421、1992)是标准化加密电子邮件的早期尝试。它包含自己的 Base64 编码(RFC 1341,用于 MIME,RFC 1421 早于规范,但在采用方面滞后)。

RFC 1421 Base64 使用字母表 A-Z、a-z、0-9、+、/(现代 Base64 字母表),并在 64 字符处换行。这个字母表避免了空格和其他有问题的字符;每个字符都可以明确打印,并且不会与控制代码或国家字符集变体混淆。 64 字符行长度与 20 世纪 80 年代纸质终端的宽度相匹配,是可读性的实际折衷方案。 Uuencode 属于周围的历史,但该工具既不读取也不写入其字母表。将其视为可互换的 Base64 将是一个格式错误。这里有用的比较仅限于用可打印字符表示字节的共同问题。

早期编码作为上下文,而不是实现证据

RFC 1421 没有被加密电子邮件广泛采用,但其 Base64 字母表保留了下来。 MIME(多用途互联网邮件扩展、RFC 2045、1996)采用了 RFC 1421 Base64 字母表,但将换行从 64 更改为 76 字符。原因不是技术性的,而是历史性的:PEM(隐私增强邮件)块是 64 字符,MIME 选择了稍微不同的限制,以避免在自动解析中与 PEM 混淆。

MIME Base64 成为电子邮件附件的标准,并且是当今使用最广泛的 Base64 变体。 RFC 2045 还定义了其他内容传输编码值(7 位、8 位、可引用打印),为邮件系统提供基于内容类型的选项。字母表选择避免了 ASCII 和 EBCDIC(IBM 大型机字符编码)之间不同的字符。字符 A-Z、a-z、0-9、+ 和 / 在两种编码中相同。 PEM 样式的块是可识别的,因为标签围绕着包裹的编码材料。 ToolAcre 可以在删除这些标签后处理提取的 Base64 正文。它无法确定哪个档案规范首先使用给定的约定,并且本文并不假装源代码树回答了该问题。

PEM 式装甲作为现代可观察格式,无需声明起源故事

开括号和闭括号等字符在 ASCII 和 EBCDIC 之间有所不同,因此被排除在外。这在 20 世纪 80 年代和 90 年代初非常重要,当时大型机到 Unix 的数据传输很常见。字母表还避免了反斜杠、单引号和双引号,这些在 C 字符串和 shell 语法中具有特殊含义。 Base64 字符串可以嵌入到 C 程序或 shell 脚本中,而无需转义几乎每个字符。

RFC 3548 (2006) 合并 Base64、base32 和 base16 编码。它指出,MIME、PEM 和其他应用程序都使用类似的概念,但具有不同的填充规则和字母表。 RFC 4648(2006,与 RFC 3548 一起发布)是当前标准,它定义了五个编码系列,每个系列都有测试向量。 RFC 还记录了历史:哪些文档定义了哪些编码、版本之间发生了什么变化以及做出选择的原因。编码器的 76 字符换行选项和解码器的空白删除使 MIME 形状的样本可测试。这些实施事实并不能证明邮件标准的完整历史。它们展示了读者可以直接在面板和测试中重现的现代兼容性行为。

MIME 样式包装作为编码器选项,无需重建标准历史记录

大多数开发人员在 RFC 4648 中仅遇到 base64 和 base64url;为那些需要实施旧变体的人记录了历史记录。 Base64url(RFC 4648 部分 5)将加号替换为破折号,将斜杠替换为下划线,以避免 URL 保留字符。包含 + 和 / 的 Base64 字符串必须在 URL 中进行百分比编码(%2B 和 %2F); base64url 避免了这种情况。

JWT (JSON Web Token) 使用不带填充的 base64url。某些应用程序使用带填充的 base64url。 RFC 定义了这两种变体;这取决于应用程序的选择。这种差异就是为什么 JWT 解码器和电子邮件 Base64 解码器可能会为相同的输入字符串产生不同的输出(一个期望是 base64url,另一个期望是 base64)。字母表、填充规则和换行都是从真实系统的实际约束中产生的。可移植性最好被视为对传输字母表的限制,而不是每个符号的经过验证的传记。在常见文本系统中,字母和数字在视觉上保持熟悉,而最终标点符号在 URL 安全模式中有所不同。在没有主要证据的情况下,省略了确切的历史选择理由。

可移植性作为设计约束,而不是对单个角色选择的验证说明

选择 64 字符集是为了跨编码的可表示性;字母表由 RFC 1341 和 1421 以及 MIME 固定;填充规则来自 3 字节对齐;换行来自电子邮件传输限制。忽略此历史的实现可能会发明新的编码或忘记边缘情况。 RFC 4648 测试向量(foobar 生成 Zm9vYmFy)是验证实现是否符合标准的方法。

现代替代品如 base85(在某些情况下使用)是存在的,但由于历史势头和足够好,base64 仍然占据主导地位。 Base64 不是最紧凑的编码(base85 和 base91 更密集),但它简单、通用且经过验证。可以肯定地说的是目前的这对字母:标准结尾为加号和斜线; URL 安全替代连字符和下划线。填充和包裹是单独的选项。测试涵盖模式和缺失填充,为当前行为提供可重复的证据,而不是推断的时间顺序。

存储库证明了当前标准和 URL 安全字母表的内容

33 百分比大小开销对于大多数用途来说是可以接受的。字母表在各个实现中都是稳定的。 RFC 非常清楚,偏差通常是故意的(例如填充省略或空白处理),而不是偶然的误解。

了解 Base64 历史可以解释为什么它看起来是这样的。加号和斜杠字符是经过深思熟虑的选择,以避免不同字符编码中的歧义。填充规则来自 3 字节分组。 Base85 和 Ascii85 使用不同的组大小和字母表,并且不在实现范围内。提及他们并不会使本页面成为他们的转换器。比较它们的密度或历史记录需要超出为此模块审核的 Base64 文件之外的源和测试向量。

要点:每个字符的选择都是有原因的 — Base64 编码器和解码器如何实现由此产生的标准字母表

换行来自电子邮件。每个决定都是为了解决真实系统的实际问题。如今,Base64 主要用于历史记录无关紧要的上下文(JWT、API、数据 URI),但字母表和填充规则是通过 RFC 4648 从 MIME 和 PEM 继承的。

读取 RFC 一次并在 Base64 编码器和解码器工具中编码测试字符串将当前标准与其历史根源联系起来。只要输入达到索引 62 或 63,生成的标准字母表就可见。使用生成这些位置的示例,切换 URL 安全模式,并仅比较更改的标点符号。该实验展示了当今的格式,而不依赖于关于其发明的未经证实的故事。