Base64编码那些事

第一次真正接触到Base64编码是在大二下的时候,当时似乎是在搞一道题目的时候接触到的,但是只是说了这个编码是Base64,直接上网找了一个在线编码解码器就收工了。

接下来也陆陆续续看到过不少Base64编码的例子,直到上了研一,有一个在线的CTF的小测试,思路是先用Base64解码然后做题。当时我就很好奇,为什么他们一眼就能看出这是Base64编码的呢,但是当时也没有深究,今天想起来了就打算好好研究一下这个编码。

为什么要用Base64编码?

这其实是我当时最大的一个疑问,我明明一串”helloworld”只有10个字符的长度,经过编码之后变成了”aGVsbG93b3JsZA==”,这不是反而更长了么,对于传输什么的肯定是不利的呀。难道是为了安全性?也不对啊,任何人都可以直接随便找个工具就能还原出来明文,也不存在密钥之说。所以具体是因为什么呢?在知乎上找到了一个让我挺满意的答案:

真正的原因是二进制不兼容。某些二进制值,在一些硬件上,比如在不同的路由器,老电脑上,表示的意义不一样,做的处理也不一样。同样,一些老的软件,网络协议也有类似的问题。

主要原因就是ASCII码只有7位,而一个字节有8位,所以如果一个字节的第一个比特位是1,那么它就不是ASCII字符,各个设备对于不是ASCII码中的”字符”处理得不太一致,但是对于ASCII字符倒是挺一致的,所以就想办法把这些不是ASCII码表中的字符转成ASCII码表就好了呗。

Base64编码

于是就有了Base64编码,看名字也知道,一共用64个不同的字符来表示。具体的对应表可以见下:

1567668554845

那么现实中是如何把二进制的数据用Base64编码成数据进行传输的呢?下面举个例子:

假设有个字符串是4dp,首先将它转成二进制的(这一步在计算机中是多余的,因为计算机本来就是存储二进制的,只是为了给人看的):

1
2
0011 0100 | 0110 0100 | 0111 0000
4 | d | p

然后按照6位一组,重新分组,得到的结果是:

1
001101 | 000110 | 010001 | 110000

再在每组高位上加上两个0:

1
2
00001101 | 00000110 | 00010001 | 00110000
13 | 06 | 17 | 48

最后查阅Base64的表格,就能得到结果是:NGRw

当然实际中可能会出现只有两个字节和一个字节的情况:

  • 两个字节,比如上面的4d,一共是16个比特,分成三组,每组的长度分别是6、6、4,这三个都在高位加上两个0,最后一个再在低位上补上两个0,所以最后一个就变成了00 0100 00,最后得到的结果,还需要加上一个=,来说明最后一个少了一个,于是结果就是NGQ=
  • 一个字节,比如上面的4,一共是8个比特,分成两组,两个组的长度分别是6和2,第一组还是按照高位加上两个0处理,另外一组除了高位加上两个0外,低位再补上4个0,查表,最后再加上两个=,也就是最后一个就成了全0,得到的结果是NA==

Base128

从效率来说,因为Base64浪费掉了两个比特(所有分组都在高位上加了2个0),所以传输效率其实只有75%,那由上面可以得知,ASCII码其实是7位的,为什么不只浪费一个比特呢?其实主要是因为ASCII码表里面还有一些控制用的,这些并不能打印,所以这也导致了没有Base128,但是如果把那些控制用的排除掉的话,是有Base85这种编码的。