Base64编码详解

Base64与MIME

Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME, 在XML中存储复杂数据.

其中MIME主要使用两种编码转换方式——Quoted-printableBase64——8位的非英语字符转化为7位的ASCII字符。(关于Quoted-printable的介绍请看文章结尾备注。)

虽然这样的初衷,是为了满足电子邮件中不能直接使用非ASCII码字符的规定,但是也有其他重要的意义:

a)所有的二进制文件,都可以因此转化为可打印的文本编码,使用文本软件进行编辑;

b)能够对文本进行简单的加密。


为什么要使用Base64?
在设计这个编码的时候,我想设计人员最主要考虑了3个问题: 
1.是否加密? 
2.加密算法复杂程度和效率 
3.如何处理传输? 

    加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。 
基于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。 

    但是,如果是基于以上两点,那么我们使用最简单的恺撒法(见备注)即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。 
基于以上的一些主要原因产生了Base64编码。 


Base64的原理

base64可以用来将二进制的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号「+」,斜杠「/」,一共64个字符,等号「=」用来作为后缀用途。

Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。

 

这样说会不会太抽象了?不怕,我们来看一个例子:

 

转换前        aaaaaabb   ccccdddd   eeffffff      

转换后        00aaaaaa   00bbcccc   00ddddee   00ffffff

 

应该很清楚了吧?上面的三个字节是原文,下面的四个字节是转换后的Base64编码,其前两位均为0。

 

转换后,我们用一个码表来得到我们想要的字符串(也就是最终的Base64编码),这个表是这样的:(摘自RFC2045)

 

 

                           Table 1: The Base64 Alphabet

 

      ValueEncoding  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

 

 

让我们再来看一个实际的例子,加深印象!

 

转换前        10101101   10111010   01110110  

转换后        00101011   00011011   00101001   00110110

十进制        43     27     41     54

对应码表中的值  r        b       p       2

 

 

所以上面的24位编码,编码后的Base64值为 rbp2

解码同理,把 rbq2 的二进制位连接上再重组得到三个8位值,得出原码。

(解码只是编码的逆过程,在此我就不多说了,另外有关MIME的RFC还是有很多的,如果需要详细情况请自行查找。)

 

用更接近于编程的思维来说,编码的过程是这样的:

 

第一个字符通过右移2位获得第一个目标字符的Base64表位置,根据这个数值取到表上相应的字符,就是第一个目标字符。

然后将第一个字符左移4位加上第二个字符右移4位,即获得第二个目标字符。

再将第二个字符左移2位加上第三个字符右移6位,获得第三个目标字符。

最后取第三个字符的右6位即获得第四个目标字符。

 

在以上的每一个步骤之后,再把结果与 0x3F 进行 AND 位操作,就可以得到编码后的字符了。


算法实现 
其实在算法详解的时候基本上已经说的很清楚了。用于程序上,除去约束判断,大概可以分为如下几步几步: 
右移两位,清0……依此类推。èAND取第一个字节的后2位和第二个字节的前4位移位放入新变量中è右移两位,高两位清0è用AND取前6位,放入新的变量中è读取数据3字节 
解码的类C语言实现的算法: 
BYTE LMoveBit(int base, int MoveNum) 

BYTE result=base; 
if(MoveNum==0)return 1; 
if(MoveNum==1)return MoveNum; 
result=base<<(MoveNum-1); 
return result; 


char base64_alphabet[]= 
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/','='}; 
BYTE Base64Decode(char *base64code, DWORDbase64length) 

char buf[4]; 
int i,j; 
int k; 
int l=0; 
BYTE temp1[4],temp2; 
BYTE *Buffer=new BYTE[base64length*3/4]; 
DWORD base64a=(base64length/4)-1; 
DWORD base64b=0; 
for(;base64b<base64a+1;base64b++) 

for(i=0;i<4;i++) 

buf[i]=*(base64code+(base64b*4)+i); 
for(j=0;j<65;j++) 

if(buf[i]==base64_alphabet[j]) 

temp1[i]=j; 
break; 



i--; 
for(k=1;k<4;k++) 

if(temp1[i-(k-1)]==64){m_padnum++; continue;} 
temp1[i-(k-1)]=temp1[i-(k-1)]/LMoveBit(2,(k-1)*2); 
temp2=temp1[i-k]; 
temp2=temp2&(LMoveBit(2,k*2)-1); 
temp2*=LMoveBit(2,8-(2*k));//move 4 
temp1[i-(k-1)]=temp1[i-(k-1)]+temp2; 
Buffer[base64b*3+(3-k)]=temp1[i-(k-1)]; 


return Buffer; 

备注

Quoted-printable编码:

简单介绍一下Quoted-printable编码转换方式。它主要用于ACSII文本中夹杂少量非ASCII码字符的情况,不适合于转换纯二进制文件。
它规定将每一个8位的字节,转换为3个字符。
第一个字符是“=”号,这是固定不变的。
后面二个字符是二个十六进制数,分别代表了这个字节前四位和后四位的数值。
举例来说,ASCII码中换页键form feed)是12,二进制形式是00001100,写成十六进制就是0C,因此它的编码值为“=0C”“=”号的ASCII值是61,二进制形式是00111101,因为它的编码值是“=3D”。除了可打印的ASCII码以外,所有其他字符都必须用这种方式进行转换。
所有可打印的ASCII码字符(十进制值从33126)都保持原样不变,“=”(十进制值61)除外。

恺撒法:

密码学上有所谓“恺撒法”:是一种简单替换法,把每个字母和它在字母表中后若干个位置中的那个字母相对应。 
比如说我们取后7个位置,那么字母的一一对应就如下表所示:

明码字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZ 
密码字母表:HIJKLMNOPQRSTUVWXYZABCDEFG

于是我们就可以从明文得到密文:

明文:VENI-VIDI-VICI 
密文:CLGP-CPKP-CPJP

但是这种方法只能有26种可能性,比较简单。一般采取随机对应的方法来增加解码难度。

明码字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZ 
密码字母表:YIXDROTUGLFSMKAECQVWJZNBPH



参考:

浅谈Base64编码http://www.5dmail.net/html/2004-1-30/200413084348.htm

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

Base64笔记  http://www.ruanyifeng.com/blog/2008/06/base64.html

矛与盾的较量(4)——奇妙的Base64编码   http://www.luocong.com/articles/show_article.asp?Article_ID=17


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值