Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2412569
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: C/C++

2011-08-15 14:00:51


Base64的编码和解码


为什么要使用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采用了一种很简单的编码转换:对于待编码数据,以3个字节为单位,依次取6位数据并在前面补上两个0形成新的8位编码,由于3×8=4×6,这样3个字节的输入会变成4个字节的输出,长度上增加了1/3上面的处理还不能保证得到的字符都是可见字符,为了达到此目的,Base64制定了一个编码表,进行统一的转换,见表11.1。码表的大小为26=64,这也是Base64名称的由来。由于编码是以3个字节为单位,当剩下的字符数量不足3个字节时,则应使用0进行填充,相应地,输出字符则使用=’占位,因此编码后输出的文本末尾可能会出现12个‘=’

11.1 Base64编码表

编码

编码

编码

编码

编码

编码

编码

编码

0

A

8

I

16

Q

24

Y

32

g

40

o

48

w

56

4

1

B

9

J

17

R

25

Z

33

h

41

p

49

x

57

5

2

C

10

K

18

S

26

a

34

i

42

q

50

y

58

6

3

D

11

L

19

T

27

b

35

j

43

r

51

z

59

7

4

E

12

M

20

U

28

c

36

k

44

s

52

0

60

8

5

F

13

N

21

V

29

d

37

l

45

t

53

1

61

9

6

G

14

O

22

W

30

e

38

m

46

u

54

2

62

+

7

H

15

P

23

X

31

f

39

n

47

v

55

3

63

/


加密举例:

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

  转换前 10101101 10111010 01110110

  转换后 00101011 00011011 00101001 00110110

  十进制 43 27 41 54

  对应码表中的值 r b p 2

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

  编码的过程是这样的:

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

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

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

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

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

  若是原文的字节不够的地方可以用全0来补足,转换时Base64编码用=号来代替。这就是为什么有些Base64编码会以一个或两个等号结束的原因,但等号最多只有两个。因为:

  余数 = 原文字节数 MOD 3

  所以余数任何情况下都只可能是012这三个数中的一个。如果余数是0的话,就表示原文字节数正好是3的倍数(最理想的情况啦)。如果是1的话,为了让Base64编码是3的倍数,就要补2个等号;同理,如果是2的话,就要补1个等号。

解密举例:

假设转换后对应base64编码表的字符为:

对应码表中的值 r b p 2 ,将其转化为bsae64表中的位置,比如rbase6443号位置,那么转化后为 00101011,则有:

转换后: 00101011 00011011 00101001 00110110

变为原始数据:10101101 10111010 01110110

编码的过程是这样的:

先将源字符串转化为bsae64表中对应的位置;

第一个字符通过左移2位 或上第二个字符右移4位得到第一个目标字符;

第二个字符通过左移4位 或上第三个字符右移2位得到第二个目标字符;

第三个字符通过左移6位 或上第四个字符得到第三个目标字符;

每次四个字符转化为3个目标字节,如果源字符串的长度不等于4的倍数,可以考虑告知用户直接结束程序或者强制转换,剩下不足的则不为0

/*


* base64_encode & decode


*/




#include


#include


#include


char base64_map[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


char base64_decode_map[256] = {


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,


255, 0, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,


15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28,


29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,


49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,


255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};


unsigned char test[100]= "493712874";


unsigned char test1[100]= "gdfgd";


unsigned char encode[100], decode[100];


void base64_encode(const char *src, int src_len, char *dst)


{


int i = 0, j = 0;



for (i; (src_len - i) / 3; i += 3) {


dst[j++] = base64_map[(src[i] >> 2) & 0x3f];


dst[j++] = base64_map[((src[i] << 4) | (src[i + 1] >> 4)) & 0x3f];


dst[j++] = base64_map[((src[i + 1] << 2) | (src[i + 2] >> 6 )) & 0x3f];


dst[j++] = base64_map[src[i + 2] & 0x3f];


}


/*不足3的倍数*/


if (src_len % 3 == 1) {


dst[j++] = base64_map[(src[i] >> 2) & 0x3f];


dst[j++] = base64_map[(src[i] << 4) & 0x3f];


dst[j++] = '=';


dst[j++] = '=';


}


else if (src_len % 3 == 2) {


dst[j++] = base64_map[(src[i] >> 2) & 0x3f];


dst[j++] = base64_map[((src[i] << 4) | (src[i + 1] >> 4)) & 0x3f];


dst[j++] = base64_map[(src[i + 1] << 2) & 0x3f];


dst[j++] = '=';


}



dst[j] = '\0';


}




//若是不是4的倍数,强制解码?


void base64_decode(const char *src, int src_len, char *dst)


{


int i = 0, j = 0;



if(src_len % 4 != 0)


{


printf("sound code is not four time\n");


exit(1);


}


for (i; (src_len - i) / 4; i += 4) {


dst[j++] = base64_decode_map[src[i]] << 2 | \


base64_decode_map[src[i + 1]] >> 4;


dst[j++] = base64_decode_map[src[i + 1]] << 4 | \


base64_decode_map[src[i + 2]] >> 2;


dst[j++] = base64_decode_map[src[i + 2]] << 6 | \


base64_decode_map[src[i + 3]];


}



dst[j] = '\0';


}


int main(void)


{


printf("%s\n",test);


base64_encode(test, strlen(test), encode);


printf("%s\n",encode);


printf("%s\n",test1);


base64_encode(test1, strlen(test1), encode);


printf("%s\n",encode);



base64_decode(encode, strlen(encode), decode);


printf("%s\n",decode);


return 0;


}

参考文献:

1http://blog.chinaunix.net/u/15780/showart_43620Array.html

2http://blog.csdn.net/wangjj_016/article/details/2794245

3

阅读(5419) | 评论(0) | 转发(2) |
0

上一篇:ACM初学者

下一篇:RFC1939-POP3协议中文版

给主人留下些什么吧!~~