遇到奇怪的ruby编码:
ruby-1.9.2-p180 :618 > s = "a8dnsjg8aiw8jq".ljust(16,'=')
=> "a8dnsjg8aiw8jq=="
ruby-1.9.2-p180 :619 > s.size
=> 16
ruby-1.9.2-p180 :620 > s.unpack('m0')
ArgumentError: invalid base64
from (irb):631:in `unpack'
ruby-1.9.2-p180 :621 > s.unpack('m')
=> ["k\xC7g\xB28<j,<\x8E"]
ruby-1.9.2-p180 :622 > s.unpack('m').first.size
=> 10
ruby-1.9.2-p180 :623 > s.unpack('m').pack('m')
=> "a8dnsjg8aiw8jg==\n"
ruby-1.9.2-p180 :624 > s.unpack('m').pack('m') == s
=> false
知道为什么这不对称!?为什么'm0'(decode64_strict)根本不工作?输入字符串填充为base64字母表中的4个字符的倍数。这里是14×6比特= 84比特,即10 1/2 8比特字节,即11字节。但是解码后的字符串似乎会丢掉最后一个nybble?
我错过了一些明显的东西或者这是一个错误吗?解决方法? 比照http://www.ietf.org/rfc/rfc4648.txt
答案 0 :(得分:3)
没有对称性,因为Base64不是填充字符串的一对一映射。让我们从实际解码的内容开始。如果您以十六进制方式查看已解码的字符串(使用例如s.unpack('H*')
,则为:
6B C7 67 | B2 38 3C | 6A 2C 3C | 8E
我将每个输入块的边界添加到Base64算法:它需要3个八位字节的输入并返回4个字符的输出。所以我们的最后一个块只包含一个输入八位字节,因此结果将是4个字符,根据标准以“==”结尾。
让我们看看最后一个块的规范编码是什么。在二进制表示中8E
是10001110
。 RFC告诉我们用零填充缺失的位,直到达到所需的24位:
100011 100000 000000 000000
我创建了6位组,因为这是我们从Base64字母表中获取相应字符所需要的。第一组(100011)转换为35十进制,因此是Base64字母表中的j
。第二个(100000)是十进制32,因此是'g'。根据规则,剩下的两个字符将被填充为“==”。所以规范编码是
jg==
如果你看一下jq == now,二进制文件就是
100011 101010 000000 000000
所以区别在于第二组。但是因为我们已经知道只有前8位是我们感兴趣的(“==”告诉我们 - >&gt;我们只会从这四个字符中检索一个解码的八位字节)我们实际上只关心前两位第二组,因为组1的6位和组2的2位形成我们的解码八位组。 100011 10
再次形成我们的初始8E
字节值。剩余的16位与我们无关,因此可以被丢弃。
这也意味着为什么“严格”Base64编码的概念有意义:非严格解码将在最后丢弃任何垃圾,而严格解码将检查最后一组6中的剩余比特为零。这就是为什么你的非规范编码将被严格的解码规则拒绝。
答案 1 :(得分:2)
您链接的RFC明确表示,xx==
形式的最后四元组对应于输入序列的一个八位字节。您不能在12个中生成16位信息(两个任意八位字节),因此这里的舍入无效。
您的字符串在严格模式下被拒绝,因为jq==
无法作为正确的Base64编码过程的结果出现。长度不是3的倍数的输入序列是零填充的,并且您的字符串具有非零位,它们不能出现:
j q = =
|100011|101010|000000|000000|
|10001110|10100000|00000000|
^^^
答案 2 :(得分:2)
来自section 3.5 Canonical Encoding的RFC4648:
例如,如果输入只是基本64位编码的一个八位字节, 然后使用第一个符号的所有六位,但只使用第一个 使用下一个符号的两位。这些填充位必须设置为 通过符合编码器来实现零...
和
在某些环境中,更改至关重要 如果填充位没有,则解码器可以选择拒绝编码 已设为零。
您的最后四个字节(jq==
)将解码为这些二进制值:
100011 101010
------ --****
带下划线的位用于形成最后编码的字节(十六进制8E)。其余位(在它们下面带有星号)应该为零(编码为jg==
,而不是jq==
)。
m
解包正在原谅填充位应该为零但不是。 m0
解包不是那么宽容,因为它是允许的(参见RFC引用位中的“MAY”)。打包解压缩的结果不是对称的,因为您的编码值是非规范的,但pack
方法产生规范编码(填充位等于零)。
答案 3 :(得分:0)
感谢您对b64的详细解释。我赞成你们所有人并接受了@ emboss的回应。
但是,这不是我想要的答案。为了更好地陈述问题,它将是,
如何填充一串b64字符,以便将其解码为 通过解包('m0')填充零填充的8位字节?
根据您的解释,我现在看到这将有助于我们的目的:
ruby-1.9.2-p180 :858 > s = "a8dnsjg8aiw8jq".ljust(16,'A')
=> "a8dnsjg8aiw8jqAA"
ruby-1.9.2-p180 :859 > s.unpack('m0')
=> ["k\xC7g\xB28<j,<\x8E\xA0\x00"]
ruby-1.9.2-p180 :861 > s.unpack('m0').pack('m0') == s
=> true
唯一的问题是,未保留解码的字符串长度,但我们可以解决这个问题。