Base64编码简介
本文介绍基于rfc4648的Base64编码格式,关于如何使用Python中的base64模块快速进行编码,请参考这里。rfc4648介绍了Base64/32/16编码机制,同时废止了rfc3548。相关的资料和标准有:rfc2045定义了MIME中的Base64传输编码,rfc4880定义了OpenPGP中使用的Radix-64编码。
Base64使用64个通用的可打印字符来存储和表示二进制字数据,同时也可以进行简单的加密,生成不可读文本。Base64字符集包括大、小写英文字母,阿拉伯数字0-9,以及"+"和"/",以及作后缀填充的"="共计65个字符,其中编码时只使用前64个。如何使用这64个字符来表示二进制数据呢?这就需要在十进制的0到63以及使用的64个字符之间建立一一对应关系。具体的映射是将十进制的0到63依次与"A-Z", "a-z", "0-9", "+", "/"一一对应。在计算机中64种可能只需要6bits就能表达(26=64),因此Base64表示法实质上是以6bits为单位,使用规定的通用字符集来表示和存储二进制数据。
计算机中的数据是以字节为单位的,且8bits = 1byte。Base64中使用6bits表示一个字符,这就导致了计算机中的三个字节(8bits*3)在经过Base64编码后将变成四个字符(6bits*4),转码后的Base64字符串将比原数据长大约三分之一。Base64的转码方法也正是围绕这样的换算关系展开。
转码时只需使用一个24bits的缓冲区,依次从原数据中读入三个字节的数据,再每次从缓冲中取6bits转化成Base64字符输出。当原始数据的长度恰好为3bytes的整数倍时,直接转码。当读取到原始数据的末尾剩余数据不足3字节时,在原始二进制码末尾补0直到恰好是6bits的倍数,这时,第65个字符"="就派上用场了。如果原始文本刚好是3字节的整数倍,转码后不需要在Base64编码末尾添加任何后缀;如果最后剩余两字节,则在转码后的字串末尾添加一个"=",如果最后剩余一字节,则在转码后的字符串末尾添加两个"="。这个过程可以用下面的图表示。
情形一:文本长度刚好是3字节的整数倍
原文本(单位:字节) | M | a | n | |||||||||||||||||||||
二进制数据 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
二进制数据(补0) | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
Base64编码 | T | W | F | u |
此时转码后的字串将为:TWFu
情形二:文本比3字节的整数倍多2字节
原文本(单位:字节) |
M | a | ||||||||||||||||||||||
二进制数据 |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | ||||||||
二进制数据(补0后) |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | ||||||
Base64编码 |
T | W | E |
此时转码后的字串将为:TWE=
情形三:文本比3字节的整数倍多1字节
原文本(单位:字节) |
M | |||||||||||||||||||||||
二进制数据 |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | ||||||||||||||||
二进制数据(补0后) |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | ||||||||||||
Base64编码 |
T | Q |
此时转码后的字串将为:TQ==
经过上面规则编码后的字串解码也非常简单。不难发现,由于每一填充符"="代表二进制源码末尾添加了2bits的0,Base64编码后字串的长度将是4的整数倍,根据末尾"="的个数计算编码过程中补0的个数,就可以正常解码。另外,在电子邮件中,每行不能容纳超过76个字符,解析时注意忽略回车符。
本文介绍了标准Base64编码的格式,事实上还存在“安全url”等变种编码格式,这些变化适应特定的需求,替代字符集中的部分字符,有兴趣的话可以参考rfc4648。
Python中基于rfc3548提供了进行Base64/32/16编码的库,接口非常高效地实现了所需的编码。关于Python中的Base64编码库,请参考这里。