12 BASE64编解码¶
12.1 BASE64编码介绍¶
BASE64编码是一种常用的将十六进制数据转换为可见字符的编码。
与ASCII码相比,它占用的空间较小。BASE64编码在rfc3548中定义。
12.2 BASE64编解码原理¶
将数据编码成BASE64编码时,以3字节数据为一组,转换为24bit的二进制数,
将24bit的二进制数分成四组,每组6bit。
对于每一组,得到一个数字:0-63。然后根据这个数字查表即得到结果。
表如下:
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
比如有数据:0x30 0x82 0x02
编码过程如下:
1)得到16进制数据: 30 82 02
2)得到二进制数据: 00110000 10000010 00000010
3)每6bit分组: 001100 001000 001000 000010
4)得到数字: 12 8 8 2
5)根据查表得到结果 : M I I C
BASE64填充:在不够的情况下在右边加0。
有三种情况:
1) 输入数据比特数是24的整数倍(输入字节为3字节整数倍),则无填充;
2) 输入数据最后编码的是1个字节(输入数据字节数除3余1),即8比特,则需要填充2个"==",
因为要补齐6比特,需要加2个00;
3) 输入数据最后编码是2个字节(输入数据字节数除3余2),
则需要填充1个"=",因为补齐6比特,需要加一个00。
举例如下:
对0x30编码:
1) 0x30的二进制为:00110000
2) 分组为:001100 00
3) 填充2个00:001100 000000
4) 得到数字:12 0
5) 查表得到的编码为MA,另外加上两个==
所以最终编码为:MA==
base64解码是其编码过程的逆过程。
解码时,将base64编码根据表展开,根据有几个等号去掉结尾的几个00,
然后每8比特恢复即可。
12.3 BASE64编解码原理¶
Openssl中用于base64编解码的函数主要有:
1)编码函数
EVP_EncodeInit
编码前初始化上下文。
EVP_EncodeUpdate
进行BASE64编码,本函数可多次调用。
EVP_EncodeFinal
进行BASE64编码,并输出结果。
EVP_EncodeBlock
进行BASE64编码。
2) 解码函数
EVP_DecodeInit
解码前初始化上下文。
EVP_DecodeUpdate
BASE64解码,本函数可多次调用。
EVP_DecodeFinal
BASE64解码,并输出结果。
EVP_DecodeBlock
BASE64解码,可单独调用。
12.4 编程示例¶
示例1¶
#include <string.h>
#include <openssl/evp.h>
int main()
{
EVP_ENCODE_CTX *ectx = NULL,*dctx = NULL;
ectx = EVP_ENCODE_CTX_new();
dctx = EVP_ENCODE_CTX_new();
unsigned char in[500],out[800],d[500];
int inl,outl,i,total,ret,total2;
EVP_EncodeInit(ectx);
for(i = 0;i < 500;i ++){
memset(&in[i],i,1);
}
inl = 500;
total = 0;
EVP_EncodeUpdate(ectx,out,&outl,in,inl);
total += outl;
EVP_EncodeFinal(ectx,out+total,&outl);
total += outl;
printf("%s\n",out);
EVP_DecodeInit(dctx);
outl = 500;
total2 = 0;
ret=EVP_DecodeUpdate(dctx,d,&outl,out,total);
if(ret < 0) {
printf("EVP_DecodeUpdate err!\n");
return -1;
}
total2 += outl;
ret=EVP_DecodeFinal(dctx,d,&outl);
total2 += outl;
EVP_ENCODE_CTX_free(ectx);
EVP_ENCODE_CTX_free(dctx);
return 0;
}
/*
*
本例中先编码再解码。
编码调用次序为EVP_EncodeInit、EVP_EncodeUpdate(可以多次)和EVP_EncodeFinal。
解码调用次序为EVP_DecodeInit、EVP_DecodeUpdate(可以多次)和EVP_DecodeFinal。
注意:采用上述函数BASE64编码的结果不在一行,解码所处理的数据也不在一行。
用上述函数进行BASE64编码时,输出都是格式化输出。
特别需要注意的是,BASE64解码时如果某一行字符格式超过80个,会出错。
如果要BASE64编码的结果不是格式化的,可以直接调用函数:EVP_EncodeBlock。
同样对于非格式化数据的BASE64解码可以调用EVP_DecodeBlock函数,
不过用户需要自己去除后面填充的0。
*/
示例2¶
#include <string.h>
#include <openssl/evp.h>
int main()
{
unsigned char in[500],out[800],d[500],*p;
int inl,i,len,pad;
for(i = 0;i < 500;i ++){
memset(&in[i],i,1);
}
printf("please input how much(<500) to base64 : \n");
scanf("%d",&inl);
len = EVP_EncodeBlock(out,in,inl);
printf("%s\n",out);
p = out + len - 1;
pad = 0;
for(i = 0;i < 4;i ++) {
if(*p == '='){
pad ++;
}
p--;
}
len = EVP_DecodeBlock(d,out,len);
len -= pad;
if((len != inl) || (memcmp(in,d,len))){
printf("err!\n");
}
printf("test ok.\n");
return 0;
}