在base64编码时删除尾随“=”

Remove trailing “=” when base64 encoding

我注意到每当我base64编码一个字符串时,末尾都会附加一个"="。我可以删除这个字符,然后通过重新添加它来可靠地解码它吗?还是这很危险?换句话说,是始终附加"=",还是仅在某些情况下附加"="?

我希望我的编码字符串尽可能短,这就是为什么我想知道我是否总是可以删除"="字符,并在解码前将其添加回去。


=是填充的。

维基百科说

An additional pad character is
allocated which may be used to force
the encoded output into an integer
multiple of 4 characters (or
equivalently when the unencoded binary
text is not a multiple of 3 bytes) ;
these padding characters must then be
discarded when decoding but still
allow the calculation of the effective
length of the unencoded text, when its
input binary length would not be a
multiple of 3 bytes (the last non-pad
character is normally encoded so that
the last 6-bit block it represents
will be zero-padded on its least
significant bits, at most two pad
characters may occur at the end of the
encoded stream).

如果控制另一端,则可以在传输时将其移除,然后在解码前重新插入(通过检查字符串长度)。请注意,传输中的数据将不是有效的base64。


我编写了Apache的commons-codec-1.4.jar base64解码器的一部分,在这个逻辑中,我们可以不用填充字符。文件结束和流结束都是指示base64消息完成的好指标,就像任何数量的"="字符一样!

我们在commons-codec-1.4中引入的URL安全变体故意省略了填充字符,以使内容更小!

http://commons.apache.org/codec/apidocs/src html/org/apache/commons/codec/binary/base64.html line.478

我想更安全的答案是,"取决于解码器的实现",但从逻辑上讲,编写一个不需要填充的解码器并不难。


在JavaScript中,您可以这样做:

1
2
3
4
5
6
7
8
9
10
11
// if this is your Base64 encoded string
var str = 'VGhpcyBpcyBhbiBhd2Vzb21lIHNjcmlwdA==';

// make URL friendly:
str = str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');

// reverse to original encoding
if (str.length % 4 != 0){
  str += ('===').slice(0, 4 - (str.length % 4));
}
str = str.replace(/-/g, '+').replace(/_/g, '/');

另请参见此小提琴:http://jsfiddle.net/7bjat/66/


填充时增加了=。base64字符串的长度应该是4的倍数,因此根据需要添加1或2个=

阅读:不,你不应该移除它。


在Android上,我使用的是:

全球的

1
String CHARSET_NAME ="UTF-8";

编码

1
2
3
4
String base64 = new String(
            Base64.encode(byteArray, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_CLOSE | Base64.NO_WRAP),
            CHARSET_NAME);
return base64.trim();

译码

1
2
byte[] bytes = Base64.decode(base64String,
            Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_CLOSE | Base64.NO_WRAP);

在Java上等于这个:

编码

1
2
3
4
5
6
private static String base64UrlEncode(byte[] input)
{
    Base64 encoder = new Base64(true);
    byte[] encodedBytes = encoder.encode(input);
    return StringUtils.newStringUtf8(encodedBytes).trim();
}

译码

1
2
3
4
5
private static byte[] base64UrlDecode(String input) {
    byte[] originalValue = StringUtils.getBytesUtf8(input);
    Base64 decoder = new Base64(true);
    return decoder.decode(originalValue);
}

我对trailing"="没有任何问题,我也在使用bouncycastle


如果您使用的是PHP,下面的函数将用适当的填充将剥离的字符串恢复为其原始格式:

1
2
3
4
5
6
7
<?php

$str = 'base64 encoded string without equal signs stripped';
$str = str_pad($str, strlen($str) + (4 - ((strlen($str) % 4) ?: 4)), '=');

echo $str,"
";


如果您正在编码字节(固定位长度),那么填充是多余的。大多数人都是这样。

base64一次消耗6位,产生一个8位字节,它只使用6位的组合。

如果您的字符串是1字节(8位),那么您将得到12位的输出,作为8所能容纳的6的最小倍数,额外4位。如果字符串是2个字节,则必须输出18位,并额外输出2位。对于6的倍数与8的倍数,可以有0、2或4位的余数。

填充表示忽略这些额外的4(==)或2(=)位。填充在那里,告诉解码器关于填充的信息。

编码字节时不需要填充。base64编码器可以简单地忽略总计少于8位的剩余位。在这种情况下,你最好把它取下来。

填充可能对流和任意长度的位序列有一定的用途,只要它们是2的倍数。它也可以用于人们只想在剩余位都为零时发送最后4位的情况。有些人可能想用它来检测不完整的序列,尽管它不太可靠。我从来没有在实践中看到过这种优化。人们很少有这种情况,大多数人使用base64作为离散字节序列。

如果你看到建议继续使用的答案,这不是一个很好的鼓励如果你只是在编码字节,这是为一组你没有的情况启用一个特性。在这种情况下启用它的唯一原因可能是为没有填充的情况下无法工作的解码器添加公差。如果你能控制两端,那就不需要担心了。


使用python,可以删除base64填充,然后像这样重新添加:

1
2
3
4
5
from math import ceil

stripped = original.rstrip('=')

original = stripped.ljust(ceil(len(stripped) / 4) * 4, '=')