上传文件
  1. base64hub
  2. 学习 Base64
  3. Base64 标准与 RFC
参考

Base64 标准:从 PEM、MIME 到 RFC 4648

Base64 没有单一的发明者,也没有单一的规范。它诞生于 1987 年的安全电子邮件,随后被 MIME 采用,直到 2003 年才有了独立的规范。下面是定义它的文档,以及这些文档衍生出的各种变体。

时间线

年份文档贡献
1987RFC 989(PEM 第一部分)64 字符的“可打印编码”,用 = 填充,另用 * 标记未加密部分
1988、1989RFC 1040、RFC 1113PEM 修订草案
1992RFC 1341(MIME)Base64 成为电子邮件的 Content-Transfer-Encoding
1993RFC 1421(PEM)、RFC 1521(MIME)PEM 每行恰好 64 个字符,MIME 修订版
1996RFC 2045(MIME)沿用至今的 MIME 定义:每行最多 76 个字符
1997RFC 2152(UTF-7)在 Unicode 文本编码中使用 Base64
2003RFC 3501(IMAP4rev1)用 , 表示邮箱名称的修改版 Base64
2003RFC 3548第一份独立规范:base64、base64url、base32、base16
2006RFC 4648取代 RFC 3548,新增 base32hex 和测试向量
2007RFC 4880(OpenPGP)带 CRC-24 校验和的 Radix-64 封装
2015RFC 7468PEM 风格证书和密钥文件的严格规则
2015RFC 7515(JWS)令牌使用不带填充的 Base64URL
2024RFC 9580(OpenPGP)取代 RFC 4880,弃用封装校验和

PEM:Base64 的起点

我们今天使用的字母表最早出现在 1987 年 2 月发布的 RFC 989《Privacy Enhancement for Internet Electronic Mail》中。加密邮件要经过只能处理可打印文本的网关,所以 PEM 需要一种办法,用在任何邮件系统中都不会被改动的字符来书写二进制密文。它每次取 6 个比特,映射到 A-Z、a-z、0-9、+ 和 /,用 = 做填充。RFC 989 中的表格与本站的字母表完全相同。它还保留了 * 来标记未加密的部分,这个特性后来没有保留下来。

PEM 经历了 RFC 1040 和 RFC 1113,最终定型于 RFC 1421(1993 年 2 月)。这份文档固定了行长:除最后一行外,每行恰好包含 64 个可打印字符,最后一行不超过 64 个。给出的理由是方便打印,以及 SMTP 每行 1000 字符的限制。PEM 作为安全邮件系统从未流行起来,但它的文件格式活了下来。每个 -----BEGIN CERTIFICATE----- 块都是它的后代,RFC 7468(2015)如今把规则写得很明确:生成方必须恰好在 64 个字符处换行,解析方可以接受其他宽度。可以用证书解码器查看这类文件。

MIME:每个邮件附件背后的 Base64

MIME(1992 年的 RFC 1341、1993 年的 RFC 1521、1996 年的 RFC 2045)需要把图片和文档放进电子邮件,于是借用了 PEM 的编码。RFC 2045 至今仍是电子邮件的参考标准,它与 PEM 有两处不同:

  • 编码后的每行不得超过 76 个字符,行与行之间用 CRLF 分隔。
  • “base64 编码数据中,任何不属于 base64 字母表的字符都应被忽略。”解码器会跳过换行、空格以及其他不属于字母表的字符。

正是第二条规则,导致很多解码器会悄悄丢弃无效字符。可以用 MIME Base64 编码器生成 MIME 风格的输出,附件的结构见 电子邮件中的 Base64。

RFC 3548 与 RFC 4648:统一的参考

在 2003 年之前,每个用到 Base64 的协议都指向 MIME 或 PEM,不管愿不愿意,都继承了它们的行长规则和宽松的解码规则。RFC 3548(2003 年 7 月)把 base64、base64url、base32 和 base16 定义为独立的编码。RFC 4648(2006 年 10 月)以少量改动取代了它:新增了能保持编码数据排序顺序的 base32hex,以及一组测试向量:

BASE64("")       = ""
BASE64("f")      = "Zg=="
BASE64("fo")     = "Zm8="
BASE64("foo")    = "Zm9v"
BASE64("foobar") = "Zm9vYmFy"

除非引用它的规范另有说明,RFC 4648 的规则比 MIME 更严格:

  • 换行:实现不得添加换行(第 3.1 节)。
  • 填充:实现必须包含 = 填充(第 3.2 节)。
  • 字母表以外的字符:解码器必须拒绝它们(第 3.3 节)。
  • 规范编码:编码器必须把未使用的比特置为零,解码器可以拒绝未置零的输入(第 3.5 节)。否则 SGk/Pg== 和 SGk/Ph== 都会解码为 Hi?>。

“除非引用本文档的规范明确另有说明”这句话起了很大作用。正因为有它,JWS 可以去掉填充,MIME 可以保留 76 字符的行长,而两者用的仍然都是“RFC 4648 Base64”。Base32 和 Base16 工具实现了同一份 RFC 中的另外两种编码。

其他标准中定义的变体

JWS 与 JWT:不带填充的 Base64URL

RFC 7515(2015 年 5 月)把 BASE64URL(x) 定义为第 5 节的字母表,并“省略所有末尾的 ‘=’ 字符”,不含换行和空白。JWT、JWE、JWK 以及 JOSE 家族的大多数成员都使用这种形式。详见 JWT 中的 Base64。

IMAP:用逗号的修改版 UTF-7

IMAP 邮箱名称是 7 位字符串,所以 RFC 3501(第 5.1.3 节)用“修改版 UTF-7”来编码非 ASCII 的文件夹名。连续的非 ASCII 字符会先转换为 UTF-16,再以不带填充的 Base64 编码,用 , 代替 /,并用 & 和 - 包起来。RFC 自带的示例 &U,BTFw- 就是中文名称“台北”,德语文件夹 Entwürfe 则变成 Entw&APw-rfe。普通的 UTF-7(RFC 2152)把同一个名称写作 Entw+APw-rfe。

OpenPGP:带 CRC-24 的 Radix-64

OpenPGP 的 ASCII 封装(armor)就是 MIME Base64 加上一行校验和。RFC 4880 定义了针对二进制数据的 CRC-24(生成多项式 0x864CFB,初始值 0xB704CE),结果用 Base64 编码并加上 = 前缀。GnuPG 对 12 个字节的 Hello world! 生成如下内容:

-----BEGIN PGP ARMORED FILE-----
Comment: Use "gpg --dearmor" for unpacking

SGVsbG8gd29ybGQh
=s4Gu
-----END PGP ARMORED FILE-----

RFC 4880 允许每行最多 76 个字符,GnuPG 写的是 64 个。RFC 9580(2024 年 7 月)保留了这种格式,但规定 CRC-24 这一行“不应生成”,并且实现不得因为它缺失或错误而拒绝一个块。

Base64 变体一览表

变体索引 62 和 63填充行长定义于
标准 Base64+ /必须无RFC 4648 第 4 节
Base64URL- _可选,由引用规范决定无RFC 4648 第 5 节
JWS / JWT- _从不无RFC 7515
MIME+ /必须最多 76,CRLFRFC 2045
PEM+ /必须恰好 64RFC 1421、RFC 7468
OpenPGP 封装+ /必须最多 76(GnuPG:64)RFC 4880、RFC 9580
UTF-7+ /无不适用RFC 2152
IMAP 修改版 UTF-7+ ,无不适用RFC 3501
bcrypt字母表 ./A-Za-z0-9无不适用事实标准(OpenBSD)
crypt(3) 哈希字母表 ./0-9A-Za-z无不适用事实标准(Unix)

最后两行的整个字母表都重新排列过,字母表页面对此有介绍。还有很多格式直接引用上表中的某一行,不做任何改动:HTTP Basic 认证(RFC 7617)、data URI(RFC 2397)、XML Schema 的 base64Binary 以及子资源完整性(SRI)哈希,用的都是带填充的标准 Base64。

常见问题

Base64 是哪个 RFC 定义的?

通用参考是 RFC 4648(2006 年 10 月),它定义了 base64、base64url、base32、base32hex 和 base16。MIME 版本见 RFC 2045,PEM 的原始版本见 RFC 1421。

为什么 MIME 每行 76 个字符,而 PEM 每行 64 个?

两个限制都是为了让每行远低于 SMTP 的 1000 字符上限。PEM 在 RFC 1421 中固定为 64;MIME 在自己的规范里选择了“不超过 76”。RFC 4648 则规定,除非引用它的标准另有要求,编码器根本不应添加换行。

Base64URL 是正式标准吗?

是的。它是 RFC 4648 的第 5 节“Base 64 Encoding with URL and Filename Safe Alphabet”,最早出现在 RFC 3548 中。JWS(RFC 7515)使用不带填充的 Base64URL。

PGP 块末尾那行 =XXXX 是什么?

它是解码后数据的 CRC-24 校验和,本身也用 Base64 写成。RFC 9580(2024)规定新的实现不应再生成这一行,也不得因为它而拒绝一个块。

继续阅读