全部博文(298)
分类: 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进行填充,相应地,输出字符则使用‘=’占位,因此编码后输出的文本末尾可能会出现1至2个‘=’。
表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
所以余数任何情况下都只可能是0,1,2这三个数中的一个。如果余数是0的话,就表示原文字节数正好是3的倍数(最理想的情况啦)。如果是1的话,为了让Base64编码是3的倍数,就要补2个等号;同理,如果是2的话,就要补1个等号。
解密举例:假设转换后对应base64编码表的字符为:
对应码表中的值 r b p 2 ,将其转化为bsae64表中的位置,比如r在base64的43号位置,那么转化后为 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;
}
参考文献:
【1】http://blog.chinaunix.net/u/15780/showart_43620Array.html
【2】http://blog.csdn.net/wangjj_016/article/details/2794245
【3】