BASE64的实现
原由
项目中经常需要使用base64进行处理,通过base64可以将特殊字符转化为普通可见字符,便于网络传输,代价是增长了传输长度。
base64将每3个byte转化为4个6bit位,然后高位补两个零。这意味着,base64编码后长度会变长。
- 当源文长度不是3的倍数时,需要补零。编码后以等号“=”表示。至多有1至2个补零的情况出现,则结尾最多一至两个等号。
- 编码后长度可预测4*(len+2)/3。
- base64是个可逆编码。
- 源文长度预估如下:末尾等号个数为p(此时p小于等于2),编码长度l (此时l为4的倍数),则源文长度 ( l * 3/4 - p)
源文 | 源长度 | base编码后 | 编码后长度 |
abc | 3 | YWJj | 4 |
abca | 4 | YWJjYQ== | 8 |
abcab | 5 | YWJjYWI= | 8 |
abcabc | 6 | YWJjYWJj | 8 |
源码请见:https://github.com/fpzeng/aes128
C语言实现
base64的源码其实没有几行,但是在项目代码中添加base64源码总有造轮子的嫌疑。这里使用libopenssl库。
1 #include <stdio.h> 2 #include <string.h> 3 #include <openssl/bio.h> 4 #include <openssl/evp.h> 5 #include <stdio.h> 6 #include <math.h> 7 8 int Base64Encode(const char* message, char** buffer) { //Encodes a string to base64 9 BIO *bio, *b64; 10 FILE* stream; 11 int encodedSize = 4*ceil((double)strlen(message)/3); 12 *buffer = (char *)malloc(encodedSize+1); 13 14 stream = fmemopen(*buffer, encodedSize+1, "w"); 15 b64 = BIO_new(BIO_f_base64()); 16 bio = BIO_new_fp(stream, BIO_NOCLOSE); 17 bio = BIO_push(b64, bio); 18 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line 19 BIO_write(bio, message, strlen(message)); 20 BIO_flush(bio); 21 BIO_free_all(bio); 22 fclose(stream); 23 24 return (0); //success 25 } 26 int calcDecodeLength(const char* b64input) { //Calculates the length of a decoded base64 string 27 int len = strlen(b64input); 28 int padding = 0; 29 30 if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are = 31 padding = 2; 32 else if (b64input[len-1] == '=') //last char is = 33 padding = 1; 34 35 return (int)len*0.75 - padding; 36 } 37 38 int Base64Decode(char* b64message, char** buffer) { //Decodes a base64 encoded string 39 BIO *bio, *b64; 40 int decodeLen = calcDecodeLength(b64message), 41 len = 0; 42 *buffer = (char*)malloc(decodeLen+1); 43 FILE* stream = fmemopen(b64message, strlen(b64message), "r"); 44 45 b64 = BIO_new(BIO_f_base64()); 46 bio = BIO_new_fp(stream, BIO_NOCLOSE); 47 bio = BIO_push(b64, bio); 48 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer 49 len = BIO_read(bio, *buffer, strlen(b64message)); 50 //Can test here if len == decodeLen - if not, then return an error 51 (*buffer)[len] = '\0'; 52 53 BIO_free_all(bio); 54 fclose(stream); 55 56 return (0); //success 57 } 58 59 int main() { 60 //Encode To Base64 61 char* base64EncodeOutput; 62 char* pPlainText = "Hi, I'm fpzeng"; 63 printf("Input plain text: %s\n", pPlainText); 64 Base64Encode(pPlainText, &base64EncodeOutput); 65 printf("Output (base64): %s\n", base64EncodeOutput); 66 67 //Decode From Base64 68 char* base64DecodeOutput; 69 Base64Decode(base64EncodeOutput, &base64DecodeOutput); 70 printf("Output: %s\n", base64DecodeOutput); 71 72 return(0); 73 }
Makefile
.PHONY : clean OPT=-O0 DEBUG= -g CFLAGS=-Wall -fPIC $(XCFLAGS) $(INC) $(OPT) $(SO_DEF) $(DEBUG) $(DEF) INCLUDES = -I./ SRCS = base64.c OBJS = $(SRCS:.c=.o) LIBS = -lssl -lcrypto -lm MAIN = base64 all: $(MAIN) @echo $(MAIN) has been compiled $(MAIN): $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS) @echo output: $(OBJS) .c.o: $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
clean: @echo clean $(MAIN) $(OBJS) rm -fr $(MAIN) $(OBJS)