145

base64编码中填充的目的是什么。以下是维基百科的摘录:

“分配了一个额外的填充字符,可用于将编码输出强制为 4 个字符的整数倍(或等效地,当未编码的二进制文本不是 3 个字节的倍数时);然后在解码时必须丢弃这些填充字符,但仍然允许计算未编码文本的有效长度,当它的输入二进制长度不是 3 字节的倍数时(最后一个非填充字符通常被编码,因此它代表的最后一个 6 位块将为零-在其最低有效位上填充,编码流的末尾最多可能出现两个填充字符)。”

我写了一个程序,它可以对任何字符串进行base64编码并解码任何base64编码的字符串。padding 解决了什么问题?

4

4 回答 4

277

您认为不需要填充的结论是正确的。总是可以从编码序列的长度中明确地确定输入的长度。

但是,填充在 base64 编码字符串以这样一种方式连接的情况下很有用,即单个序列的长度会丢失,例如,在一个非常简单的网络协议中可能会发生这种情况。

如果连接未填充的字符串,则无法恢复原始数据,因为有关每个单独序列末尾的奇数字节数的信息会丢失。但是,如果使用填充序列,则没有歧义,并且可以正确解码整个序列。

编辑:插图

假设我们有一个程序对单词进行 base64 编码,将它们连接起来并通过网络发送。它对“I”、“AM”和“TJM”进行编码,将结果夹在一起而不进行填充并传输它们。

  • I编码为SQSQ==带填充)
  • AM编码为QU0QU0=带填充)
  • TJM编码为VEpNVEpN带填充)

所以传输的数据是SQQU0VEpN。接收器 base64 将其解码为I\x04\x14\xd1Q)预期的IAMTJM. 结果是无稽之谈,因为发送者已经破坏了关于每个单词在编码序列中结束位置的信息。如果发送者已经发送SQ==QU0=VEpN,接收者可以将其解码为三个单独的 base64 序列,它们将连接起来给出IAMTJM

为什么要使用 Padding?

为什么不设计协议来为每个单词添加一个整数长度的前缀呢?然后接收器可以正确解码流并且不需要填充。

这是一个好主意,只要我们在开始编码之前知道我们正在编码的数据的长度。但是,如果我们不是文字,而是对来自实时摄像机的视频块进行编码呢?我们可能事先不知道每个块的长度。

如果协议使用填充,则根本不需要传输长度。数据可以在从相机传入时进行编码,每个块都以填充终止,并且接收器将能够正确解码流。

显然这是一个非常人为的例子,但也许它说明了为什么填充在某些情况下可能会有所帮助。

于 2014-10-29T13:55:40.243 回答
53

在相关说明中,这是我为您创建的任意基本转换器。享受! https://convert.zamicol.com/

什么是填充字符?

填充字符有助于满足长度要求并且没有任何意义。

填充的十进制示例: 给定所有字符串长度为 8 个字符的任意要求,数字 640 可以使用前面的 0 作为填充字符来满足此要求,因为它们没有任何意义,“00000640”。

二进制编码

字节范式:字节是事实上的标准度量单位,任何编码方案都必须与字节相关。

Base256完全符合这种范式。一个字节等于 base256 中的一个字符。

Base16,十六进制或十六进制,每个字符使用 4 位。一个字节可以表示两个 base16 字符。

与 base256 和 base16 不同, Base64并不完全适合字节范式(base32 也不适用)。所有 base64 字符都可以用 6 位表示,不足 2 位是一个完整字节。

我们可以将 base64 编码与字节范式表示为分数:每字符 6 位超过每字节 8 位。减少这个分数是 3 个字节超过 4 个字符。

这个比例,每 4 个 base64 字符占 3 个字节,是我们在编码 base64 时要遵循的规则。 Base64 编码甚至只能保证使用 3 字节包进行测量, 这与 base16 和 base256 不同,其中每个字节都可以独立存在。

那么为什么即使没有填充字符编码也能正常工作,为什么还要鼓励填充呢?

如果流的长度未知,或者如果准确知道数据流何时结束可能会有所帮助,请使用填充。填充字符明确表示那些额外的点应该是空的,并排除任何歧义。即使长度未知,您也会知道数据流的结束位置。

作为一个反例,像JOSE这样的一些标准不允许填充字符。在这种情况下,如果缺少某些东西,加密签名将不起作用或其他非 base64 字符将丢失(如“.”)。尽管没有做出关于长度的假设,但也不需要填充,因为如果出现问题,它根本就不起作用。

这正是base64 RFC 所说的,

在某些情况下,不需要或不使用在基本编码数据中使用填充 ("=")。在一般情况下,当无法对传输数据的大小做出假设时,需要填充以产生正确的解码数据。

[...]

如果执行不当,base 64 中的填充步骤 [...] 将导致编码数据的非显着更改。例如,如果输入对于 base 64 编码只有一个八位字节,则使用第一个符号的所有六位,但只使用下一个符号的前两位。这些填充位必须通过符合要求的编码器设置为零,这在下面的填充描述中进行了描述。如果此属性不成立,则没有基本编码数据的规范表示,并且可以将多个基本编码字符串解码为相同的二进制数据。如果此属性(以及本文档中讨论的其他属性)成立,则保证规范编码。

填充允许我们以不丢失位的承诺来解码 base64 编码。如果没有填充,则不再明确确认以三字节包进行测量。如果没有填充,您可能无法保证在没有额外信息的情况下准确再现原始编码,这些信息通常来自堆栈中的其他位置,如 TCP、校验和或其他方法。

例子

这是 RFC 4648 的示例表单(https://www.rfc-editor.org/rfc/rfc4648#section-8

“BASE64”函数中的每个字符使用一个字节(base256)。然后我们将其转换为 base64。

BASE64("")       = ""           (No bytes used. 0%3=0.)
BASE64("f")      = "Zg=="       (One byte used. 1%3=1.)
BASE64("fo")     = "Zm8="       (Two bytes. 2%3=2.)
BASE64("foo")    = "Zm9v"       (Three bytes. 3%3=0.)
BASE64("foob")   = "Zm9vYg=="   (Four bytes. 4%3=1.)
BASE64("fooba")  = "Zm9vYmE="   (Five bytes. 5%3=2.)
BASE64("foobar") = "Zm9vYmFy"   (Six bytes. 6%3=0.)

这是您可以使用的编码器:http ://www.motobit.com/util/base64-decoder-encoder.asp

于 2013-08-29T18:36:19.763 回答
8

在现代,它没有太多好处。因此,让我们将其视为原始历史目的可能是什么的问题。

Base64 编码在 1993 年的RFC 1421中首次出现。该 RFC 实际上专注于加密电子邮件,base64 在一个小节 4.3.2.4中进行了描述。

该 RFC 没有解释填充的目的。我们最接近提及原始目的的是这句话:

完整的编码量始终在消息结束时完成。

它不建议连接(此处为最佳答案),也不建议将易于实施作为填充的明确目的。然而,考虑到整个描述,假设这可能是为了帮助解码器以 32 位单位(“量子”)读取输入,这并非不合理。这在今天没有任何好处,但是在 1993 年,不安全的 C 代码很可能实际上利用了这个属性。

于 2011-03-21T11:01:00.877 回答
0

填充以定义的方式将输出长度填充为四个字节的倍数。

于 2022-01-01T11:15:53.937 回答