Base64加解密详解

来源:互联网 发布:淘宝千牛教程 编辑:程序博客网 时间:2024/04/27 21:59

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;
}

原创粉丝点击