什么是Base64编码?它的用途是什么?

1026

我听过人们零散地谈论"base 64编码"。它通常用于什么?


9
base64_encode() 的手册中得到的信息是:“这种编码方式旨在使二进制数据能够经过非 8 位清洁的传输层,例如邮件正文,而不被破坏。” - still_dreaming_1
19个回答

1216
当您需要在网络上传输某些二进制数据时,通常不会只以原始格式流式传输位和字节。为什么呢?因为有些媒体是用于流式传输文本的。您永远无法确定——某些协议可能会将您的二进制数据解释为控制字符(例如调制解调器),或者您的二进制数据可能会出错,因为底层协议可能认为您已输入特殊字符组合(例如FTP将换行符转换为另一种形式)。
因此,人们将二进制数据编码为字符之一。Base64是这些编码类型之一。
为什么是64个字符?因为您可以通常依赖许多字符集中存在相同的64个字符,并且您可以相当自信地确保您的数据最终能够未损坏地到达目标方。

125
理论上你可以使用一些类似于80进制的编码方式,但这将更加困难。二的幂次方是二进制的自然基数。 - Jon Skeet
15
@yokees说:“没有保证,它们只是几乎总是安全的字符。这就是为什么有多种形式的Base-64(http://en.wikipedia.org/wiki/Base-64)。" - user565869
10
那是否意味着所有网络类型的数据传输都应该使用某种编码方式? - Tanner Summers
7
为什么要使用base64方法来编码字符串数据?例如在JavaScript中的atob函数。服务器对JSON文件进行Base64编码是否有意义?特殊字符可能是一种用例,但为什么不使用utf8,在这种情况下它们是否等效?如有进一步资源,将不胜感激。谢谢。 - partizanos
6
如果有人知道的话,最好提供至少一些可能失败的协议清单。 - tedi
显示剩余11条评论

269

该技术基本上是一种将任意二进制数据编码为ASCII文本的方法。每3个字节的数据需要4个字符进行编码,最后可能会有一些位填充。

实际上,输入的每6个比特被编码在一个64个字符的字母表中。 "标准" 字母表使用 A-Z、a-z、0-9,以及 + 和 /,= 作为填充字符。还有一些URL安全的变体。

维基百科 是一个相当好的获取更多信息的资源。


5
加密或压缩,以及声音/图片/视频的结果。 - Jon Skeet
1
@CholthiPaulTtiopic:我不太清楚你所说的“存储”是什么意思,但我认为我们现在有些跑题了。 - Jon Skeet
4
强烈建议不要以“字符串二进制”的方式思考。二进制数据应该被视为二进制数据,而不是文本。我在Stack Overflow上看到了数百甚至数千个问题,基本上就是由于人们没有足够重视这种区别而引起的。 - Jon Skeet
3
PHP称它们为“二进制字符串”。(来源)http://php.net/manual/en/function.pack.php - Cholthi Paul Ttiopic
1
@AlirezaAhmadi:不是标准的base64。 - Jon Skeet
显示剩余5条评论

230

多年前,当邮件功能被引入时,它完全是基于文本的。随着时间的推移,像图片和媒体附件(音频、视频等)这样的需求出现了。当通过互联网发送这些附件时(基本上是以二进制数据的形式),原始二进制数据损坏的概率很高。因此,BASE64应运而生。

二进制数据的问题在于它包含空字符,在某些语言(如C、C++)中表示字符字符串的结尾,因此以原始形式发送包含NULL字节的二进制数据将停止文件被完全读取并导致数据损坏。

例如:

在C和C++中,“null”字符表示字符串的结尾。 因此,“HELLO”存储如下所示:

H E L L O

72 69 76 76 79 00

00 表示“到此为止”。

现在让我们深入了解BASE64编码的工作原理。

值得注意的一点:字符串长度应该是3的倍数。

例1:

要编码的字符串:“ace”,长度=3

  1. 将每个字符转换为十进制数。

a=97,c=99,e=101

enter image description here

  1. 将每个十进制数转换为8位二进制表示。

97=01100001,99=01100011,101=01100101

综合:01100001 01100011 01100101

  1. 将其分为6位一组。

011000 010110 001101 100101

  1. 将二进制转换为十进制。

011000= 24, 010110= 22, 001101= 13, 100101= 37

  1. 使用base64表将十进制字符转换为base64。

24= Y, 22= W, 13= N, 37= l

“ace” => “YWNl”

enter image description here

示例2:

要编码的字符串:“abcd”,长度= 4,不是3的倍数。因此,为使字符串长度为3的倍数,我们必须添加2位填充以使长度= 6。填充位由“=”符号表示。

注意事项:一个填充位等于两个零00,因此两个填充位等于四个零0000。

因此,让我们开始这个过程:-

  1. 把每个字符转换成十进制。

a = 97,b = 98,c = 99,d = 100

  1. 将每个十进制数转换为8位二进制表示。

97= 01100001,98= 01100010,99= 01100011,100= 01100100

  1. 将其分为6位一组。

011000,010110,001001,100011,011001,00

因此,最后的6位不完整,所以我们插入两个填充位,它们等于四个零“0000”。

011000,010110,001001,100011,011001,000000 ==

现在,它是相等的。两个等号表示添加了4个零(有助于解码)。

  1. 计算二进制到十进制。

011000= 24,010110= 22,001001= 9,100011= 35,011001= 25,000000=0 ==

  1. 使用base64表将十进制字符转换为base64。

24= Y,22= W,9= j,35= j,25= Z,0= A ==

"abcd" => "YWJjZA=="


7
我知道我们不应该在这里添加评论,比如:“这是一个非常好的答案”,但是... 这确实是一个非常好的答案。 - Bob Horn

155

Base-64 编码是一种将二进制数据转换为文本的方法,以便在电子邮件和 HTML 表单数据等传输中更容易传输。

http://zh.wikipedia.org/wiki/Base64


132

这是一种将二进制数据转化为文本编码的方法,结果文本只包含字母、数字和符号“+”、“/”和“=”。它是一种方便的方式,用于在专门用于文本数据的介质上存储/传输二进制数据。

但是为什么选择Base-64呢?将二进制数据转换为文本的两个替代方法:

  1. 十进制: 将每个字节的十进制值存储为三个数字: 045 112 101 037等,其中每个字节由3个字节表示。数据膨胀了三倍。
  2. 十六进制: 将字节存储为十六进制对: AC 47 0D 1A等,其中每个字节由2个字节表示。数据膨胀了两倍。

Base-64将3个字节(8 x 3 = 24位)映射到4个跨越6位(6 x 4 = 24位)的字符中。结果看起来像"TWFuIGlzIGRpc3Rpb..."。因此,膨胀仅为原始数据的 4/3 = 1.3333333 倍。


14
我理解得对吗:64是最好的选择,因为它是最高的2的幂,可以转换为可打印的ASCII字符(总共有95个可打印字符)? - voho
如果在这两种情况下它们都是24位,那么膨胀不是1:1吗?或者当你说4个字符跨越6位时,你是指每个字符实际上有8位,但前两位是填充的0吗? - David Klempfner
1
@Backwards_Dave 每6位以8位表述,因此膨胀比率为8:6,即4:3。 - Ates Goral
@AtesGoral,我的假设是正确的吗?当您使用Base256时,您可以进行1:1映射吗?因为1字节= 8位= 256个可能的字符? - MMMM
作为一种思维实验,可以考虑。但是使用Base256可能没有实际意义。 - Ates Goral
@ChillaBee 是的,但这样做会违背使用base64的初衷。因为当你将256个不同的字节放入电子邮件中时,邮件系统会破坏它们。 - user253751

102

除了已经提到的内容,还有两个常见的用途没有被列出:

哈希:

哈希是一种单向函数,可以将一个字节块转换为另一个固定大小(如128位或256位(SHA/MD5))的字节块。将结果字节转换为Base64使得显示哈希更加容易,特别是在比较完整性校验和时。哈希在Base64中经常出现,因此许多人错误地将Base64本身视为哈希。

加密:

由于加密密钥不必是文本而是原始字节,因此有时需要将其存储在文件或数据库中,这时Base64就非常方便。对于所得到的加密字节也是一样。

请注意,尽管Base64经常用于加密,但它不是安全机制。任何人都可以将Base64字符串转换回其原始字节,因此不应将其用作保护数据的手段,只能用作以更轻松的方式显示或存储原始字节的格式。

证书

x509 PEM格式证书是以Base64编码的。http://how2ssl.com/articles/working_with_pem_files/


8
在很多情况下,以字节形式存储字节实际上更容易处理。即使在数据库中,尤其是在文件中(如果使用定长记录或字节是唯一内容的情况下)。当这些字节打算被传输到某个地方时,特别是通过可能截断位或将某些字节解释为控制代码的通道,通常会使用Base64。 - cHao
我从未见过哈希以无符号8位整数0、1、255、36等形式编写,并使用UTF-8或任何其他编码显示它是没有意义的,除了使用base64之外,你还能用什么方式来显示它呢?加密密钥和加密数据通常存储在配置和XML文件中,其中你无法存储原始字节。如果可以将其存储为原始字节,则我同意,但是当你无法这样做时,base64就派上用场了。除传输之外,base64有许多用途。这些只是两种常见情况,你会看到它的应用。 - Despertar
1
你应该将哈希显示为十六进制,而不是十进制。对于哈希来说,这实际上比base64更常见。 - cHao
@cHao 是的,这也很常见。十六进制数字可以表示任何二进制数据,但是 base64 有一个优点,它使用更多字符,占用的空间要少得多。 - Despertar
你把SHA和MD5的大小搞反了;SHA通常(但不总是)是256,而MD5是128。 - The Daleks stand with Ukraine

36
在计算机早期,电话线跨系统通讯的可靠性不高,一种快速且简单的数据完整性验证方法被采用:“奇偶校验位”。在这种方法中,每个传输的字节都有7位数据,第8位要么是1,要么是0,以强制字节中1的总数为偶数。

因此,0x01将被传输为0x81;0x02将是0x82;0x03将保持为0x03等。

为了进一步发展这一系统,在定义ASCII字符集时,只分配了00-7F的字符。 (至今,范围在80-FF中的所有字符集均为非标准)

当时的许多路由器将奇偶校验和字节转换放入硬件中,强制连接到它们的计算机严格处理7位数据。 这迫使电子邮件附件(以及所有其他数据,这就是为什么HTTP和SMTP协议基于文本),转换为纯文本格式。

那个年代的很少路由器能活到90年代。我严重怀疑现在还有哪个在使用。

3
这是一个很好的讨论点和有趣的历史课,谢谢。 - Dan Bechard
1
但我认为7位ASCII的采用主要是由打孔纸带格式驱动的,它的起源在于电报而不是计算机间通信。 - Michael Kay

29

来自 http://zh.wikipedia.org/wiki/Base64

Base64是一种特定的MIME内容传输编码。它也用作一种通用术语,用于任何类似的编码方案,通过将二进制数据数值化并将其转换为基于64个字符的表示形式进行编码。选择特定的基数是由于字符集编码的历史原因:可以选择一组64个字符,这些字符既是大多数编码共同子集的一部分,也是可打印的。此组合使得数据在通过传统上不支持8位ASCII字符的系统(例如电子邮件)传输时不太可能被修改。

Base64可用于各种情境:

  • Evolution和Thunderbird使用Base64来混淆电子邮件密码[1]
  • Base64可用于传输和存储可能会导致分隔符冲突的文本
  • Base64经常用作快速但不安全的快捷方式来隐藏秘密,而不需要承担加密密钥管理的开销

  • 垃圾邮件发送者使用Base64来逃避基本的反垃圾邮件工具,这些工具通常不会解码Base64,因此无法检测编码的邮件中的关键字。

  • Base64用于在LDIF文件中对字符字符串进行编码
  • Base64有时用于使用类似于......的语法将二进制数据嵌入XML文件中,例如Firefox的bookmarks.html。
  • Base64还用于与政府财政签名打印设备(通常通过串行或并行端口)通信时,以最小化传输签名字符的延迟。
  • Base64用于在脚本中编码二进制文件(如图像),以避免依赖外部文件。
  • 可用于将原始图像数据嵌入CSS属性(例如background-image)中。

15

一些传输协议仅允许传输字母数字字符。想象一下使用控制字符来触发特殊操作和/或仅支持每个字符有限的比特宽度的情况。

Base64 将任何输入转换为一种编码,只使用字母数字字符、+/= 作为填充字符。


13
Base64是一种将二进制数据表示为ASCII字符串格式的编码方案。它旨在通过网络通道传输以二进制格式存储的数据。
Base64机制使用64个字符进行编码,其中包括:
1. 10个数字值:即0,1,2,3,...,9 2. 26个大写字母:即A,B,C,D,...,Z 3. 26个小写字母:即a,b,c,d,...,z 4. 2个特殊字符(这些字符取决于操作系统):即+,/
Base64的工作原理如下:
1. 计算字符串中的字符数。如果不是3的倍数,则使用特殊字符(即=)进行填充,使其成为3的倍数。 2. 将字符串转换为ASCII二进制格式8位使用ASCII表。 3. 转换为二进制格式后,将二进制数据分成6位一组的块。 4. 将6位二进制数据块转换为十进制数。 5. 根据base64索引表将十进制数转换为字符串。此表可作为示例,但是如我所说,2个特殊字符可能会有所不同。
现在,我们得到了输入字符串的编码版本。
让我们做一个例子:将字符串THS转换为base64编码字符串。
1. 计算字符数:它已经是3的倍数。 2. 转换为ASCII二进制格式8位。我们得到(T)01010100 (H)01001000 (S)01010011 3. 将二进制数据分成6位一组的块。我们得到010101 000100 100001 010011 4. 将6位二进制数据块转换为十进制数。我们得到21 4 33 19 5. 根据base64索引表将十进制数转换为字符串。我们得到VEhT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接