Base64来历有两说
1:Base64内容传送编码用来把 任意序列的8位字节 描述为一种不易被人直接识别的形式。
2:Base64编码多用于邮件传输中,由于在邮件传输过程中需要经过邮件网关,但是,这个邮件网关会将高位为1的字符过滤掉。需要解决这个问题便要将普通字符的8bit全都转换成6bit然后在高位上补0。因此,base64的编码原理就是将三个连续的8bit字符转换成四个6bit的字符再将里面的高2bit位致0(3×8=4×6=24),编码后的长度由于三个变四个多了一个,理论上编码后的长度比原来多了三分之一。
不管那种说法,而base64编码的解码就是编码的逆过程了。
编码原理: 将3个字节转换成4个字节, 先读入3个字节, 这样共有24位, 从第一位补偿'00'起, 每隔六位就补偿'00', 这样就有4个字节了.
编码规则:
用"A…Za…z0…9+/"64个字符来表示6位长度的二进制数值,该数值从0至63依次对应从"A"至"/"的字符,编码时3个字节3个字节进行编码,每3个字节(24位)以最高两位补0的形式分成四个字节,这样每个字节的值刚好能与"A…Za…z0…9+/"64个字符一一对应,当最后没有三个字节时,编码后则用"="号补足四个字节.
BASE64编码每行不得超过76个字符(不包含最后的"/r/n"两个字符),否则必须换行,换行方法是:在结尾处加上"/r/n".
下面举例说明Base64编码过程:
假设对"mfc"这个字符串进行BASE64编码, 设c1 = 'm', c2 = 'f', c3 = 'c'; 转换为内码就是6d6663(十六进制)
按照编码原理编码如下:
mfc三字节 -> 0x6d 0x66 0x63 -> 01101101 01100110 01100011 -> 分为四节 -> 011011 010110 011001 100011 -> 每隔六位就补偿'00' ->
00'011011 00'010110 00'011001 00'100011 -> 00011011 00010110 00011001 00100011 -> 27 22 25 35 -> 查base64EncodeChars表 -> bWZj -> 四字节
算法具体处理如下:
1、字节c1(6d)处理: c1 >> 2 -> 00011011, 值为27, 对应base64EncodeChars数组中的'b', 为编码后的第一个字节;
2、字节c1和c2处理: ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4) -> (00000001 << 4) | (01100000 >> 4) -> (00010000 | 00000110) -> 00010110,
值为22, 对应base64EncodeChars数组中的'W', 为编码后的第二个字节;
3、字节c2和c3处理: ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6) -> (00000110 << 2) | (01000000 >> 6) -> (00011000 | 00000001) -> 00011001,
值为25, 对应base64EncodeChars数组中的'Z', 为编码后的第三字节:
4、字节c3剩余部分的处理: c3 & 0x3F -> 00100011, 值为35, 对应base64EncodeChars数组中的'j', 构成编码后的第四字节.
则"mfc"Base64编码后, 就变为"bWZj"了.
解码原理:
将4个字节转换成3个字节. 先读入4字节, 这样共有32位, 每字节都扣出最高两位, 进行四次, 这样就变为24位了, 重新接合, 就是3个字节了,于是就还原了.
解码过程举例说明: 仍拿'mfc'说事儿:
四字节 -> bWZj -> 98 87 90 106 -> 查base64DecodeChars表 -> 27 22 25 35 -> 0x1b 0x16 0x19 0x23 -> 00011011 00010110 00011001 00100011 -> 分解 -> 00'011011 00'010110 00'011001 00'100011 -> 011011 010110 011001 100011 -> 再组合 -> 011011 01 0110 0110 01 100011 -> 01101101 01100110 01100011 -> 0x6d 0x66 0x63 -> mfc
算法实现如下:
1、b -> 98, 查base64DecodeChars表 得27(0x1b); W -> 87, 查base64DecodeChars表 得22(0x16); Z -> 90, 查base64DecodeChars表 得25(0x19); j -> 106, 查base64DecodeChars表 得35(0x23); 由bWZj -> 查base64DecodeChars表 -> 0x1b 0x16 0x19 0x23,
对应关系:c1 -> 00011011; c2 -> 00010110; c3 -> 00011001; c4 -> 00100011;
2、(c1 << 2) | ((c2 & 0x30) >> 4)) 为处理后的第一字节;
3、(c2 & 0X0f) << 4) | ((c3 & 0x3c) >> 2) 为处理后的第二字节;
4、(c3 & 0x03) << 6) | c4 为处理后的第三字节
这样就将"bWZj"还原为"mfc"了.
c++代码实现如下:
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdio.h>
// 编码后的字符集
char base64EncodeChars[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 对应ASICC字符的位置
int base64DecodeChars[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 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, -1, -1, -1, -1, -1,
-1, 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, -1, -1, -1, -1, -1
};
// base64编码
// 该函数的返回值需要在外部释放
char* base64encode(char* str)
{
// 加密
char* cOut = 0;
int i = 0;
int nTemp = 0;
int len = strlen(str);
char c1, c2, c3;
if (0 == len)
{
return 0;
}
// 需要对字符串进行行处理:
// BASE64编码每行不得超过76个字符(不包含最后的"/r/n"两个字符),否则必须换行,
// 换行方法是:在结尾处加上"/r/n", "/r/n"是两个字符
int nCount = len/57 * 2;
int nMemSize = (len/3 * 4) + nCount +8;
// In a C++ file, explicitly cast malloc's return
cOut = (char*)calloc(nMemSize, sizeof(char));
memset(cOut, 0, nMemSize);
int nSize = nMemSize * sizeof(char);
while (i < len)
{
// c1为char类型, 8位, 所以跟0xFF进行按位与(&)操作后, 仍旧是其自身
// 字节c1
c1 = str[i++] & 0xff;
// 当最后没有三个字节时, 编码后则用"="号补足四个字节
if (i == len)
{
// 第一个字节
nTemp = int((c1 >> 2) & 0x3f);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
// 第二个字节
nTemp = int((c1 & 0x03) << 4);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
// 第三第四为两个补偿的'='符号
sprintf_s(cOut, nSize, "%s==", cOut);
nTemp = i%57;
if (0 == nTemp)
{
sprintf_s(cOut, nSize, "%s/r/n", cOut);
}
break;
}
// 字节c2
c2 = str[i++];
if (i == len)
{
// 第一个字节
nTemp = int((c1 >> 2) & 0x3f);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
// 第二个字节
nTemp = int(((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4));
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
// 第三个字节
nTemp = int((c2 & 0x0F) << 2);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
// 第四为一个补偿的'='符号
sprintf_s(cOut, nSize, "%s=", cOut);
nTemp = i%57;
if (0 == nTemp)
{
sprintf_s(cOut, nSize, "%s/r/n", cOut);
}
break;
}
// 字节c3
c3 = str[i++];
nTemp = int((c1 >> 2) & 0x3f);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
nTemp = (int)(((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4));
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
nTemp = int(((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6));
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
nTemp = int(c3 & 0x3F);
sprintf_s(cOut, nSize, "%s%c", cOut, base64EncodeChars[nTemp]);
nTemp = i%57;
if (0 == nTemp)
{
sprintf_s(cOut, nSize, "%s/r/n", cOut);
}
}
return cOut;
}
// base64解码
// 函数返回值需要在外部释放
char* base64decode(char* str)
{
// 解密
// 需要去除字符串里面的'/r/n'
int c1, c2, c3, c4;
int i = 0;
char* cOut = 0;
int len = strlen(str);
if (0 == len)
{
return 0;
}
// 分配足量的内存
int nCount = len/76;
int nMemSize = (len/4 + 2) *3 -nCount;
cOut = (char*)calloc(nMemSize, sizeof(char));
memset(cOut, 0, nMemSize);
int nSize = nMemSize * sizeof(char);
while (i < len)
{
do
{
c1 = base64DecodeChars[str[i++] & 0xff];
} while (i < len && c1 == -1);
if (c1 == -1) break;
do
{
c2 = base64DecodeChars[str[i++] & 0xff];
} while (i < len && c2 == -1);
if (c2 == -1) break;
sprintf_s(cOut, nSize, "%s%c", cOut, ((c1 << 2) | ((c2 & 0x30) >> 4)));
do
{
c3 = str[i++] & 0xff;
// '='值为61, 遇到'='符号表示已经到了字符串的结尾
if (c3 == 61) return cOut;
c3 = base64DecodeChars[c3];
} while (i < len && c3 == -1);
if (c3 == -1) break;
sprintf_s(cOut, nSize, "%s%c", cOut, ((c2 & 0X0f) << 4) | ((c3 & 0x3c) >> 2));
do
{
c4 = str[i++] & 0xff;
// '='值为61, 遇到'='符号表示已经到了字符串的结尾
if (c4 == 61) return cOut;
c4 = base64DecodeChars[c4];
} while (i < len && c4 == -1);
if (c4 == -1) break;
sprintf_s(cOut, nSize, "%s%c", cOut, ((c3 & 0x03) << 6) | c4);
}
return cOut;
}