彻底弄懂 base64 及其原理


转自 https://mp.weixin.qq.com/s/TcSNPY1a6z8kP76usH6dCA

Base32 与 Base64

Base32 是一个 binary-to-text encoding schemes,顾名思义,就是将二进制数据转换为编码只有基础 32 个字符的数据编码方式,Base64 则是 64 个。注意编码不等同于加密,网上有误解 Base 编码方式为加密方式,实际上标准 Base64 编码解码无需额外信息即完全可逆。

Base 编码常见用途如下

如定义所言,binary to text

一些协议如 HTTPFTP (File Transfer Protocol)[当指定发送文本时], SMTP (Simple Mail Transfer Protocol)text-based protocol,也就是只支持文本传输,不支持二进制传输。是的,http 上传文件,图片时使用的 multipart/form-data 也是需要转成文本的。

所以附件如图片,文件等(binary)就可以用 Base64 编码为 text再传输。

将资源编码为字符串

data URI scheme 定义了如下语法来识别网页中的资源:

 data:[<media type>][;base64],<data>

HTML 中可以在标签中指定识别 Base64编码 来展示资源,

<div>
  <p>Taken from wikpedia</p>
  <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
</div>

但因为 Base64 是每 3 个原始字符编码成 4 个字符,不够时补 =(下文会详述),因此编码后的大小是有可能会比原文件大的,所以 html 用 Base64 来展示图片而不是用具体的图片好处大概就只有少建立一条 http 连接以及少一个 http 请求(在 HTTP 1.1 以下),这种办法只有大量的小图片才有优越性了。

统一转成『合法』字符

为了避免出现不符合规则的字符,方便把含有不可见字符串的信息用可见字符串表示出来。比如 http 协议当中的 headers 头部,必须进行 URLEncode 不然出现的等号可能使解析失败,空格也会使 http 请求解析出现问题,比如请求行也就是 request 就是以空格来划分的 POST /hi/you HTTP/1,值得注意的是 Base32 的字符列表里有不合法字符 /。

还有避免原始信息经过百花齐开的路由,网关多次转发,因有部分系统不支持此不可识别字符或将此作为控制符,将其转义、丢弃等,造成信息丢失,所以如电子邮件里的附件也是用 base64 编码的。

base64url

有 base64 编码的变种 base64url,将base64 编码中的 + 换成 - 以及将 / 换成_,甚至不需要往后面补=了。这样子在 url 中传递东西时,不再需要 URL encode,好处就是长度短了,以及好看了一点,毕竟 % 有点视觉污染(实际上,还可以直接将编码后的东西存数据库了,因为 base64 比 URLEncode 更通用了 )

Base64 的由来——参考 RFC

RFC 向来都不会说明设计的历史由来,自然 base64 编码也是一样,我参考的 rfc4648也只是说明了因为当时开发者们自己发明使用base 64并不规范,没有统一的标准,因此定义了一份通用标准。

然后呢,Base64 就是自己选了 ASCII 子集(64 个字符)为标准字符集,当然这也是因为 64 是 2的 x 次方 (如 64 就是 2 的6次方),而1个 bit 分别有 0和 1 两种状态,6 个 bit也就是 2 的 6 次方=64 个状态,刚好可以表示 64 个字符,因此 6 个 bit 就可以表达出 64 个字符了。就是下面定义的 64 个:

                      Table 1: The Base 64 Alphabet

     Value Encoding  Value Encoding  Value Encoding  Value Encoding
         0 A            17 R            34 i            51 z
         1 B            18 S            35 j            52 0
         2 C            19 T            36 k            53 1
         3 D            20 U            37 l            54 2
         4 E            21 V            38 m            55 3
         5 F            22 W            39 n            56 4
         6 G            23 X            40 o            57 5
         7 H            24 Y            41 p            58 6
         8 I            25 Z            42 q            59 7
         9 J            26 a            43 r            60 8
        10 K            27 b            44 s            61 9
        11 L            28 c            45 t            62 +
        12 M            29 d            46 u            63 /
        13 N            30 e            47 v
        14 O            31 f            48 w         (pad) =
        15 P            32 g            49 x
        16 Q            33 h            50 y

编码定义

The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters.

  • 输入:二进制(图片,文件,字符串本质就是二进制)
  • 输出:编码后的字符串
  • 处理过程:处理输入的二进制时,每 24 个 bit (3 个字节)作为一组,编码输出为 base64 处理后的 4 个标准字符集中的字符。

值得注意的是,网上的示例或说明中,都或多或少有以下偏颇之处:

  • 输入的例子可以是16 进制数字、二进制、一串数字等,很多文章举的例子都是字符串;让人忽略 binary to text 的 binary
  • 是每 24 位(同样需要注意不一定是 3 个 8 位的字符,3 个字节bytes才准确)为一组来处理,输出 4 个编码后的字符。强调这点是因为,24 位为一组,不够的都需要补 =,如按其他人的文章说的 8 位 8 位的转,根本不清楚要补多少 =
  • 24 位转成 4 个编码后的字符(也就是 4*8=32位),所以编码后的长度肯定会变大
  • 综上所述,RFC 原文才是最对的定义,有时细微的区别意味着理解有问题。下面会一一说明。

特殊处理

When fewer than 24 input
bits are available in an input group, bits with value zero are added
(on the right) to form an integral number of 6-bit groups.
Padding at the end of the data is performed using the '=' character.

  • 每 24 位为一组来编码输入的 binary 时,如果最后的一组不足24 位,往后补 0直到 补足到 24
  • 对于最后对于全为 0 的一组,补充 =

举一些例子来说明一下:

 Input data:  0x14fb9c03d97e
      16进制:     1   4    f   b    9   c     | 0   3    d   9    7   e
      2进制:    00010100 11111011 10011100  | 00000011 11011001 01111110
      6位一组:  000101 001111 101110 011100 | 000000 111101 100101 111110
      Decimal: 5      15     46     28       0      61     37     62
      Output:  F      P      u      c        A      9      l      +

16 进制的 0x14fb9c03d97e 作为输入,先转成二进制,然后 2 进制的每 24 位 选出来编码,上面例子就是:00010100 11111011 10011100,然后 6 位一组的分开,得到 000101 001111 101110 011100

然后分别转 10 进制,也就是 000101 变成 5,001111 变成 15等,再去 base64 定义的字符列表中找出此 10 进制对应的字符,以此类推,就是 base64 后的结果了。

上面例子是输入刚好是有48 位, 2个 24 位,刚刚够,不需要补 =

下面看看需要补 = 的例子:

Input data:  0x14fb9c03
      Hex:     1   4    f   b    9   c     | 0   3
      8-bit:   00010100 11111011 10011100  | 00000011 开始补 0 =》00000000 00000000
                                             pad
      6-bit:   000101 001111 101110 011100 | 000000 110000 000000 000000
      Decimal: 5      15     46     28       0      48
                                                  pad with =      =
      Output:  F      P      u      c        A      w      =      =

注意上述输入只有 32 位,第一个 24 位处理完后,还剩下 8 位,因此需要补16 个 0.

补完后,就是 48 位的输入了,照样每 24 位输出 4 个编码后的字符。

观察后半部分,000000 110000 000000 000000,第一个 000000 因为后面还有内容,所以10 进制为 0,因此编码字符为 A,这个很正常;而 1100000 之后的两个 6 位 0,都是纯粹的填充(pading)了,因此并不用 A 而都用 = 代替掉,注意不用 A

Base64 decode

说完 encode,decode 就容易啦,无非就是逆过程。

一串 base64 后的字符串,根据每个字符在 base64 字符表里找到对应的 10 进制,然后转成 2 进制,最后多余补足的 000000 去掉。


参考:

https://www.lucidchart.com/techblog/2017/10/23/base64-encoding-a-visual-explanation/

https://www.liaoxuefeng.com/wiki/1016959663602400/1017684507717184
https://www.zhangxinxu.com/wordpress/2018/08/js-base64-atob-btoa-encode-decode/
https://www.wikiwand.com/en/Binary-to-text_encoding
https://www.wikiwand.com/en/Data_URI_scheme
https://tools.ietf.org/html/rfc4648#page-3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • 1. 什么是Base64 Base64是一种基于64个可打印字符来表示二进制数据的表示方法 Base64是一种编码...
    理查德成阅读 2,918评论 0 2
  • 每个文本编辑器都有默认的编码方式(比如 UTF-8 编码),当我们保存文档的时候,可以选择编码方式,如果没有特意选...
    _于曼丽_阅读 1,519评论 0 1
  • 一个偶然的机会,我在帅sir的朋友圈里看到了彭小六老师的这本书——《让未来现在就来》。可能是怀着想要改变自己的想法...
    几个番茄阅读 465评论 3 6
  • 影子在静静发呆 鸟儿在啁啾徘徊 我也在茫茫人海 滚滚红尘中醒来 任思绪缓缓轻盈 缱绻在眉宇间 烙印在心海 不惊然间...
    大贵贵丿阅读 321评论 15 15
  • 从zine到印象笔记到简书。短短两年里,想法却空前地多。 “活在当下”。一直所信奉的原则。 不知道是否是正确的事情...
    轩轩的大宝贝阅读 188评论 0 0