# base64编码的原理

  • base64不是加密,就是一种编码,将字符串的二进制按6个bit一组,每组的6个bit的形式转换一下。

  • 每组6个bit对应的字符是 有特定的映射表决定。

映射表:

字符 序号
A-Z 序号0-25
a-z 序号26-51
0-9 序号52-61
+ 序号62
/ 序号63
  • 都转成以上5种,目的是为了网络传输没有特殊字符。

  • 标准base64 不适合url中直接传输; safe_base64的做法是把最后那俩字符 +和/ 替换成 -_ 即可。解析的时候反向替换之后再base64解码。

  • 因为http传输,+号会被服务器解析成空格, 导致解析base64字符串失败。当然可以对传输的base64字符串进行url编码在传输, nginx会自动url解码。

  • c语言实现编码(3个byte拆分成4个byte):

  • 3个字节一次循环正好拆为4个字符,如果循环剩余1个字符,需要补2个=,如果剩余2个字符,需要补1个=。每个=是6位,凑成8位的倍数,因为网络传输是按8位一组传输的,所以必须是总bit数必须是8的倍数。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


char* base64_encode(const char *p, int length, char **output, int *dlen){
    static const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int j = 0;
    int flag = 0;
    const char *str = p;
    int insize = length;
    char * dst = *output;
    int i = 0;
    while(i < insize){
        unsigned char c1 = (unsigned char) *str++;
        i++;

        c1 = c1 & 0xff ;
        if(i == insize){
            snprintf(*output, 5, "%c%c==",base[((c1 & 0xfc) >> 2)], 
                                        base[((c1 & 0x03) << 4)]);
            *output +=4;
            break;
        }
        unsigned char c2 = (unsigned char) *str++;
        i++;
        if(i == insize){
            snprintf(*output, 5, "%c%c%c=",base[((c1 & 0xfc) >> 2)], 
                                        base[(((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4))], 
                                        base[((c2 & 0x0f) << 2)]);
            *output +=4;
            break;
        }

        unsigned char c3 = (unsigned char) *str++;
        i++;
        snprintf(*output, 5, "%c%c%c%c",base[((c1 & 0xfc) >> 2)], 
                                    base[(((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4))], 
                                    base[(((c2 & 0x0f) << 2) | ((c3 & 0xc0) >>6))],
                                    base[(c3 & 0x3f)]);
        
        *output +=4;
    }
    **output = '\0';
    *dlen = strlen(dst);

    return dst;
}


int find_index(unsigned char c){
    static const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int i = 0;
    int ret = 0;
    for(i=0; i< 64; i++){
        if(base[i] == c){
            ret = i;
            break;
        }
    }

    return ret;
}

char*   base64_decode(const char *src, int len,  char **output, int *dlen){
    char *str = (char*)src;
    int i = 0;
	char *dst =  *output;
	
    while(i < len){
        int arr[4] = {0};
        int k = 0;
        while(k<4){
            char tmp = *str++;
            if(tmp != '='){
                arr[k] = find_index(tmp);
            }
            k++;
        }
        i+=4;
        //printf("%d, %d,%d,%d\n",arr[0],arr[1],arr[2],arr[3] );

        unsigned char o1 = ((arr[0]<<2) | (arr[1] & 0x30) >>4);
        unsigned char o2 = (((arr[1] & 0x0f) << 4) | ((arr[2] & 0x3c) >>2));
        unsigned char o3 = (((arr[2] & 0x03) << 6) | arr[3]);
        snprintf(*output, 4, "%c%c%c", o1,o2,o3);
        //printf("%c,%c,%c\n", o1,o2,o3);
        *output+=3;
    }

    //printf("%s \n", base64);
    *dlen = strlen(dst);
    return dst;
}


int main(int argc, char* argv[]){
    
    if(argc != 3){
        printf("提示: 输入加密选项-d或-e, 和待加密解密的参数");
        exit(-1);
    }
    if(!strcmp(argv[1], "-e")){
        int len = strlen(argv[2]);
        char *output = (char*)malloc(((len+2)/3)*4);
        char *encode_str = output;

        int dlen = 0;
        base64_encode(argv[2], len, &output, &dlen);
        printf("字符串:%s\n", argv[2]);
        printf("base64编码: %s\n", encode_str);
        free(encode_str);
    }else if(!strcmp(argv[1],"-d")){
        //const char * encode_str = "aGVsbG93b3JsZA=="; // helloworld
        char *encode_str = argv[2];
        int len = strlen(encode_str);
        char *output = (char *)malloc(((len+3) /4) *3);
        char *decode_str = output;
        int dlen = 0;
        base64_decode(encode_str, len,  &output, &dlen);
        printf("base64解码: %s\n", decode_str);
        free(decode_str);
    }else{
        printf("命令选项错误: -[de]\n");
    }

    return 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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  • lua ffi编译
cd /usr/local/openresty/lualib/base64
gcc -o base64.so -fpic -shared base64.c

1
2
3
  • openresty 接口调用
local ffi = require "ffi"
local base64 = ffi.load("/usr/local/openresty/lualib/base64/base64.so")

ffi.cdef[[
	int base64_encode(const char *p, int length, char **output, int *dlen);
	int base64_decode(const char *src, int len,  char **output, int *dlen);
]]

local c_char = ffi.typeof("char[?]")
local c_int = ffi.typeof("int[?]")

local function base64_encode(s) 
	local buf = ffi.new(c_char, ((#s+2) / 3 ) * 4)
	local buf1= ffi.new("char *[1]", buf) -- 二级指针,如果不修改指针指向,用一级指针即可。
	local dlen = ffi.new(c_int, 1) -- ffi.new("int[1]")
	local encode_str = base64.base64_encode(s, #s, buf1, dlen)

	return ffi.string(buf, dlen[0])
end



local function base64_decode(s)
	local buf = ffi.new(c_char, ((#s+3) / 4 ) * 3)
	local buf1= ffi.new("char *[1]", buf) -- 二级指针,如果不修改指针指向,用一级指针即可。
	local dlen = ffi.new(c_int, 1) -- ffi.new("int[1]")
	local encode_str = base64.base64_decode(s, #s, buf1, dlen)
	return ffi.string(buf, dlen[0])
end

local encode = base64_encode("真的很用容易去调用一个外部 C 库函数,示例代码")
ngx.say(encode)
local decode = base64_decode(encode)
ngx.say(decode)

--[[
output:
55yf55qE5b6I55So5a655piT5Y676LCD55So5LiA5Liq5aSW6YOoIEMg5bqT5Ye95pWw77yM56S65L6L5Luj56CB
真的很用容易去调用一个外部 C 库函数,示例代码
]]--

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41