Convert.FromBase64String的奇怪行为

时间:2015-03-26 20:09:41

标签: c# base64

为什么以下代码

var s =  "2I==";
var b = Convert.FromBase64String(s);
var new_s = Convert.ToBase64String(b);

最终new_s为2A==

s最初是一个较长的字符串(96个字符)但我无法包含它,因为它是一个密钥。

2 个答案:

答案 0 :(得分:5)

" 2I =="根据{{​​3}}表示数字54,8(填充x2)。

换句话说,代表的位是:

110110 000100 XXXXXX XXXXXX

(其中X表示"我不在乎,它来自填充")

但是,因为填充表示此处只有一个字节的信息,所以第二个字符的最后4位是无关紧要的。与以往一样,我们可以将4个6位信息重新格式化为3个8位信息,此时它变得更加清晰:

11011000 0100XXXX XXXXXXXX

您可以看到第二个字节必须是填充,因为它的一些位来自填充字符。因此,只有第一个字符和第二个字符的前两位是相关的 - 它只解码为单个字节0b11011000。

现在当你编码 0b11011000时,你知道你有两个填充字符,第一个字符必须是' 2' (代表比特' 110110')但第二个字符可以是任何字符,其前两位代表' 00'。恰好Convert.ToBase64String使用' A',其中0位用于不相关的部分。

我心中的问题是为什么编码器会选择使用' I'而不是'。我不会认为在Base64中执行此操作无效,但这是一个奇怪的选择。

答案 1 :(得分:4)

Jon Skeet为观察到的行为提供了一个很好的解释。但是,在Base64的大多数定义下,您的输入字符串将被视为无效。标准包含以下文本:

  

当输入组中有少于24个输入位时,将添加值为零的位(在右侧),以形成一个6位组的整数。

  • RFC 1421:加强互联网电子邮件的隐私保护
  • RFC 2045:多用途Internet邮件扩展(MIME)
  • RFC 4648:Base16,Base32和Base64数据编码

RFC 4648进一步强调了这一点:

  

如果不正确,基础64和基本32编码中的填充步骤可以      实施后,导致编码数据的非重大改变。      例如,如果输入只是一个基本64位编码的八位字节,      然后使用第一个符号的所有六位,但只使用第一个      使用下一个符号的两位。这些填充位必须设置为      通过符合编码器[...]零      如果填充位没有,则解码器可以选择拒绝编码      已设为零。

我们可以假设您的原始输入由一个具有值2160xD8)的字节组成。二进制:

11011000

这需要分成6位组:

110110 00

并且,根据上面引用的定义,最后一组需要用零填充:

110110 000000

根据Base64字母,110110(十进制:54)映射到字符2,而000000(十进制:0)映射到字符A。添加=填充以获取24位组,最终结果将为2A==。这是原始输入的唯一有效编码。