Base64编解码算法详解(附C/C++源码)[转自CSDN]

摘要:
Base64不是一种新的算法,但如果你没有从事过页面开发,你对它了解不多,但听起来很熟悉。对于黑客来说,Base64算法与MD5算法具有相同的地位,因为电子邮件正文由Base64编码。Base64实现了将任意字节转换为可读字符的编码。如果没有绝对的安全加密,Base64不是为了安全,而是为了显示。而且,Base64是可逆的,即可以通过简单的解密获得原始文本。

Base64不是什么新奇的算法了,不过如果你没从事过页面开发(或者说动态页面开发,尤其是邮箱服务),你都不怎么了解过,只是听起来很熟悉。
对于黑客来说,Base64与MD5算法有着同样的位置,因为电子邮箱(e-mail)正文就是base64编码的。
那么,我们就一起来深入的探讨一下这个东东吧。

对于一种算法,与其问“它是什么?”,不如问“它实现了什么?”
Base64实现了:将任意字节转为可读字符的编码。
我们知道,除了页面上的文本,计算机中的数据还有很多是不可见的。那么我们再扯一扯编码的问题吧。

通俗的说,编码就是给某个文字符号边上一个数字序号,计算机在现实这个文字符号(字符)的时候,根据这个序号到字库中查找对应的点阵或矢量数据,
在显示器上“画”出来。(关于点阵和矢量我们就不扯了,不然就真的太远了。)

起初的字符编码,没有把汉字、日文、朝鲜文和其他文字包括在内,只有26个英文字母的大小写和10个阿拉伯数字。加上一些控制字符和空格,用一个字节
就能够完全的编码了。(不要告诉我你不知道2的7次方和2的8次方是多少,一个技术人员为这样的问题困扰简直是一种耻辱。)

然而,世界上除了文字还有数据,比如图片、压缩文件、程序等等都是二进制文件,这些文件一样以字节为单位存储数据,这些字节往往不仅仅是2的7次方以内
的可显示的文字字符编码,还有可能是大于127(有符号数小于0)的字节,这些字节没办法用字符显示出来,Base64就是通过某种算法将他们显示出来。

*那么,Base64加密是安全的吗?
没有绝对安全的加密,Base64不是为了安全,而是为了显示。而且Base64是可逆的,也就是说,通过简单的解密就能得到原文。其实即便是不可逆的MD5算法,
也可以通过明文数据库找出可能的原文(睡到知道e10adc3949ba59abbe56e057f20f883e的原文就是123456)。

*那么,Base64是怎么实现的呢?
其实很简单,不过为了URL等特殊用处,Base64选择了以下64个字符作为密文显示,着64个字符都是可显示的,他们是:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
如果密文有不属于他们的字符,那么不是Base64编码或者是山寨版的。

一眼看去就知道是26个字母大小写和数字,加上“+”“/”两个符号,“?*-”由于正则表达式的问题,没有选用,而空格和回车这些是不能显示的。
Base64处理的过程是,以3个字节为一组(3个字节就是24位嘛),每6位扩展成8位得到4个字节(就是32位):
11111111 11111111 11111111 -> 111111 111111 111111 111111 -> 00111111 00111111 00111111 00111111
那么,得到的每一个字节,最大也就是2的6次方。也许你说:哇,小于2的7次方,可以显示了。
其实不是,得到的这2的6次方式上面那一串字符的索引,也就是说每个字节的值只是代表它在密文表中的位置,比如
字符“a”的编码是97, 用16进制表示是0x61(VB表示为&H61),二进制:01100001,因为不足3位,补0得到 00011000 00010000 000……
前两个字节是十进制的24 和 16,那么对应那一串字符中的第24个字符和第16个字符为:YQ(索引从0开始算),那么单独“a”的Base64编码为
YQ==(不足3为的每个字符直接转为“=”),简单吧!

有了算法,解码的过程就各位聪明特达的程序员取思考思考吧,最后C/C++版的编码和解码代码贴上!

/******************************************
  Base64编码解码算法 C语言源代码
  by 虎胆游侠 http://blog.csdn.net/prsniper
******************************************/
#include <stdio.h>	//注意哦,VC中""是当前路径,<>是系统路径
#include <windows.h>

const char BASE_CODE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//编码,参数:要编码的字符串指针,解码后存放的位置(编码字串长度的4/3),要编码的字符串长度 ->返回结果长度
int fnBase64Encode(char *lpString, char *lpBuffer, int sLen) 
{	register int vLen = 0;	//寄存器局部变量,提速
	while(sLen > 0)		//处理整个字符串
	{	*lpBuffer++ = BASE_CODE[(lpString[0] >> 2 ) & 0x3F];	//右移两位,与00111111是防止溢出,自加
		if(sLen > 2)	//够3个字符
		{	*lpBuffer++ = BASE_CODE[((lpString[0] & 3) << 4) | (lpString[1] >> 4)];
			*lpBuffer++ = BASE_CODE[((lpString[1] & 0xF) << 2) | (lpString[2] >> 6)];
			*lpBuffer++ = BASE_CODE[lpString[2] & 0x3F];
		}else
		{	switch(sLen)	//追加“=”
			{	case 1:
					*lpBuffer ++ = BASE_CODE[(lpString[0] & 3) << 4 ];
					*lpBuffer ++ = '=';
					*lpBuffer ++ = '=';
					break;
				case 2:
					*lpBuffer ++ = BASE_CODE[((lpString[0] & 3) << 4) | (lpString[1] >> 4)];
					*lpBuffer ++ = BASE_CODE[((lpString[1] & 0x0F) << 2) | (lpString[2] >> 6)];
					*lpBuffer ++ = '=';
					break;
			}
		}
		lpString += 3;
		sLen -= 3;
		vLen +=4;
	}
	*lpBuffer = 0;
	return vLen;
}

//子函数 - 取密文的索引
inline char GetCharIndex(char c) //内联函数可以省去函数调用过程,提速
{	if((c >= 'A') && (c <= 'Z'))
	{	return c - 'A';
	}else if((c >= 'a') && (c <= 'z'))
	{	return c - 'a' + 26;
	}else if((c >= '0') && (c <= '9'))
	{	return c - '0' + 52;
	}else if(c == '+')
	{	return 62;
	}else if(c == '/')
	{	return 63;
	}else if(c == '=')
	{	return 0;
	}
	return 0;
}

//解码,参数:结果,密文,密文长度
int fnBase64Decode(char *lpString, char *lpSrc, int sLen)   //解码函数
{	static char lpCode[4];
	register int vLen = 0;
	if(sLen % 4)		//Base64编码长度必定是4的倍数,包括'='
	{	lpString[0] = '\0';
		return -1;
	}
	while(sLen > 2)		//不足三个字符,忽略
	{	lpCode[0] = GetCharIndex(lpSrc[0]);
		lpCode[1] = GetCharIndex(lpSrc[1]);
		lpCode[2] = GetCharIndex(lpSrc[2]);
		lpCode[3] = GetCharIndex(lpSrc[3]);

		*lpString++ = (lpCode[0] << 2) | (lpCode[1] >> 4);
		*lpString++ = (lpCode[1] << 4) | (lpCode[2] >> 2);
		*lpString++ = (lpCode[2] << 6) | (lpCode[3]);

		lpSrc += 4;
		sLen -= 4;
		vLen += 3;
	}
	return vLen;
}

void main()	//主函数,测试函数
{	char s[] = "a", str[32];
	int l = strlen(s);
	int i = fnBase64Encode(s, str, l);
	printf("fnBase64Encode(\"%s\", str, %d) = \"%s\";\n", s, l, str);
	l = fnBase64Decode(s, str, i);
	printf("fnBase64Decode(\"%s\", s, %d) = \"%s\";\n", str, i, s);
}

  

转自:http://blog.csdn.net/prsniper/article/details/7097643

免责声明:文章转载自《Base64编解码算法详解(附C/C++源码)[转自CSDN]》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Python代码编写规范,你真的会吗?ML——决策树模型下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OpenSSL简单介绍及在Windows、Linux、Mac系统上的编译步骤

OpenSSL介绍:OpenSSL是一个强大的安全套接字层password库,囊括基本的password算法、经常使用的密钥和证书封装管理功能及SSL协议。并提供丰富的应用程序供測试或其他目的使用。 SSL是SecureSockets Layer(安全套接层协议)的缩写,能够在Internet上提供秘密性传输。其目标是保证两个应用间通信的保密性和可靠性,...

CentOS设置密码复杂度及过期时间等

我们在使用linux系统设置密码的时候,经常遇到这样的问题,系统提示:您的密码太简单,或者您的密码是字典的一部分。那么系统是如何实现对用户的密码的复杂度的检查的呢?  系统对密码的控制是有两部分(我知道的)组成:  1 cracklib  2 login.defs  声明:login.defs主要是控制密码的有效期。对密码进行时间管理。此处不细谈。  lo...

java 使用qrcode生成二维码图片或者base64字符串

通过传入字符串,生成二维码图片或者base64格式字符串 1 public static String barcode2Base64(String msg) throwsException{ 2 Qrcode x = newQrcode(); 3 //N代表数字,A代表a-z,B代表其他字符 4 x.setQ...

【转载】C#中将字符串分割成字符数组

在C#中字符串类型String是由一系列的单个字符组合而成,其实可以通过字符串String对象ToCharArray()方法来将字符串中的元素逐一存在数据类型为Char的一维数组中。 例如将字符str = "ABCDEFG"分割为到一维数组可用下列语句: string str = "ABCD"; char[] strCharArr = str...

多串匹配

meteor多串匹配 Description Input 第一行为一个整数n,表示文本的长度 第二行为一个长度为n的文本 第三行为一个整数m,表示模式串个数 下接m行,每行一个模式串  Output 共m行,若第i个模式串在文本中出现过则第i行输出YES,否则输出NO 数据范围 对于30%的数据,n<=10^3,m<=10^3; 对于80%的数...

SQL Server:SQL Like 通配符特殊用法:Escape

 SQL Server:SQL Like 通配符特殊用法:Escape   %:匹配零个及多个任意字符; _:与任意单字符匹配; []:匹配一个范围; [^]:排除一个范围 Symbol Meaning like '5[%]' 5% like '[_]n' _n like '[a-cdf]' a, b, c, d, or f like...