Base64算法的简介与实现
1. Base64算法的简介
1) 简述
该算法被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式,达到一眼望去完全看不出内容。此算法的复杂程度要小,效率高。如果是基于以上两点,那么我们使用最简单的单字母代替法等即可,实际上Base64要稍微复杂些,这是因为在Email的传送过程中,由于历史原因,Email只被允许传送ASCII字符,即一个8位字节的低7位。
2) 具体描述
把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在每个6位的前面补两个0,形成8位,即一个字节。再根据每个字节的值,用下表中的值替换,不足4个字节的,补“=”。例如,高山(0xB8DFC9BD),先将(0xB8DFC9)分割成四个字节(0x2E0D3F09),用下表值替换为(uN/J);再将(0xBD)分割成(0x2F10)用下表值替换为(vQ),再补两个(=),合起来就形成了编码后的课件字符串(uN/J vQ==)。
2. Base64算法的实现
1) 编码函数
char gEncBase64(unsigned char* indata, unsigned long inlen,\
unsigned char *outdata, unsigned long *outlen);//编码
功能:对长度为inlen的输入数据indata进行base64编码,编码后的数据存放在提前开辟好的内存outdata中去,其长度outlen不得小于((inlen+2)/3)*4。
参数:
indata:要进行编码的数据的地址指针。
Inlen:要进行编码的数据的长度。
Outdata:编码后数据存放的地址指针。
Outlen:传入outdata的长度,传出编码后的数据的长度。
返回值:成功时,返回编码后的数据的地址,失败时,返回NULL。
2) 解码函数char gDecBase64(unsigned char* indata, unsigned long inlen,\
unsigned char *outdata, unsigned long *outlen);//解码
功能:对长度为inlen的输入数据indata进行base64解码,解码后的数据存放在提前开辟好的内存outdata中去,其长度outlen不得小于inlen*3/4。
参数:
indata:要进行解码的数据的地址指针。
Inlen:要进行解码的数据的长度。
Outdata:解码后数据存放的地址指针。
Outlen:传入outdata的长度,传出解码后的数据的长度。
返回值:成功时,返回解码后的数据的地址,失败时,返回NULL。
3. 源代码
1) 头文件
#ifndef _gjs_BASE64_H
#define _gjs_BASE64_H
char* gEncBase64(char* data,unsigned long inlen,unsigned long *outlen);//编码
char* gDecBase64(char* data,unsigned long inlen,unsigned long *outlen);//解码
#endif
2) 源文件
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "ctype.h"
const char gBase[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="};
static union
{//为了方便操作
struct
{
unsigned long d:6;
unsigned long c:6;
unsigned long b:6;
unsigned long a:6;
} s;
unsigned char c[3];
} u;
char *gEncBase64(char* data,unsigned long inlen,unsigned long *outlen)
{//编码
char *p=NULL,*ret=NULL,*p1=NULL;
int tlen=0,i=0,j=0;
if(data==NULL || inlen==0)
return NULL;//传入参数不对
tlen=inlen/3;
if(inlen%3!=0)
tlen=tlen+1;
tlen=tlen*4;
*outlen=tlen;//编码后的数据长度
if((ret=(char*)malloc(tlen+1))==NULL)
return NULL;//内存开辟失败
memset(ret,0,tlen+1);
p=data;p1=ret;tlen=inlen;
while(tlen>0)
{//循环编码
u.c[0]=u.c[1]=u.c[2]=0;
for(i=0;i<3;i++)
{//取3字节
if(tlen<=0) break;
u.c[2-i]=(char)*p;
tlen--;p++;
}
if(i==0)
break;//没取到
switch(i)
{//根据取到的字节数分情况
case 1://取到1字节
*p1=gBase[u.s.a];p1++;
*p1=gBase[u.s.b];p1++;
*p1=gBase[64];p1++;//'=';
*p1=gBase[64];p1++;//'=';
break;
case 2://取到2字节
*p1=gBase[u.s.a];p1++;
*p1=gBase[u.s.b];p1++;
*p1=gBase[u.s.c];p1++;
*p1=gBase[64];p1++;//'=';
break;
case 3://取到3字节
*p1=gBase[u.s.a];p1++;
*p1=gBase[u.s.b];p1++;
*p1=gBase[u.s.c];p1++;
*p1=gBase[u.s.d];p1++;
break;
default:
break;
}
}
return ret;
}
char* gDecBase64(char* data,unsigned long inlen,unsigned long *outlen)
{//解码
int c1,c2,c3,c4;
char ch1=0,ch2=0,ch3=0,ch4=0,*p=NULL,*ret=NULL;
char *p1=NULL,*p2=NULL,*p3=NULL,*p4=NULL;
int tlen=0,rlen=0;
if(data==NULL || inlen==0)
return NULL;//传入参数错误
tlen=inlen*3/4;
if((ret=(char*)malloc(tlen+1))==NULL)
return NULL;//内存开辟失败
memset(ret,0,tlen+1);
p=data;tlen=inlen;rlen=0;
while(tlen>0)
{//循环解码
if(tlen>0)
{//取第1位
ch1=*p;p++;tlen--;
if((p1=(char*)strchr(gBase,ch1))==NULL)//不在表中
return 0;
}
if(tlen>0)
{//取第2位
ch2=*p;p++;tlen--;
if((p2=(char*)strchr(gBase,ch2))==NULL)//不在表中
return 0;
}
if(tlen>0)
{//取第3位
ch3=*p;p++;tlen--;
if((p3=(char*)strchr(gBase,ch3))==NULL)//不在表中
return 0;
}
if(tlen>0)
{//取第4位
ch4=*p;p++;tlen--;
if((p4=(char*)strchr(gBase,ch4))==NULL)//不在表中
return 0;
}
if((ch1==gBase[64])||(ch2==gBase[64])
||(ch3==gBase[64] && ch4!=gBase[64]))
return 0;//编码有错误
u.s.a=p1-gBase;
u.s.b=p2-gBase;
if (ch3==gBase[64])
{//原数据只有1个字节
*(ret+rlen)=(char)u.c[2];rlen++;
}
else if (ch4==gBase[64])
{//原数据只有2个字节
u.s.c=p3-gBase;
*(ret+rlen)=(char)u.c[2];rlen++;
*(ret+rlen)=(char)u.c[1];rlen++;
}
else
{//原数据有3个字节
u.s.c=p3-gBase;
u.s.d=p4-gBase;
*(ret+rlen)=(char)u.c[2];rlen++;
*(ret+rlen)=(char)u.c[1];rlen++;
*(ret+rlen)=(char)u.c[0];rlen++;
}
}
*outlen=rlen;
return ret;
}