Base64 加密解密的 C 语言实现和 python 实现

本文介绍了如何使用C语言和Python实现Base64加密解密。通过详细解析C语言和Python的代码实现过程,展示了从3*8位到4*6位的转换以及解密时的4*6位到3*8位的转换。同时,提供了命令行用法示例,帮助读者更好地理解和应用这些函数。
摘要由CSDN通过智能技术生成

Base64 加密解密的 C 语言实现和 python 实现

由于逆向中涉及 base64 加密解密很多次了,每次都不能一眼看出来,所以自己用 pythonC 语言各仿照着写了一遍流程,希望能给以后加深影响。

#include <string.h> 
#include <stdio.h> 
#include <stdlib.h> 
char *base64_encode(char *str)	//接收要加密的字符串
{
	int str_len; //获取输入的字符的长度,用于后续是否是3的倍数的操作
	int len;	//不足3的倍数是直接下表添加'='
	int i,j;	//用于后续3位一组为单位拆分3*8为4*6的后续操作
	char *encodestr;	//定义用于接受加密后的字符串
	char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //定义base64的基本表单元素,用指针是因为C语言的字符串没法像python一样可以直接对字符串进行非地址的截取。
	str_len=strlen(str);	//获取输入的字符串长度
	if (str_len % 3 == 0)
		len=(str_len/3)*4;	//如果输入字符是3的倍数,就不用补0,那按照3拆4的规则,加密后的字符串的长度就是len
	else
		len=((str_len/3)+1)*4;	//这里的str_len/3会取整数部分,如果输入的不是3的倍数,比如多一个就是4,多两个也是4。
	encodestr=malloc(sizeof(char)*len+1);	//因为C语言不像python一样可以直接+=来添加字符串,所以要用列表,这里是为列表赋予足够的加密字符空间,加1是因为后面的'\0'结束符。
	encodestr[len]='\0';	//写入结束符
	for(i=0,j=0;i<len-1;j+=3,i+=4)	//开始3*8变4*6的拆分循环,i<len-1是因为最后一个字符是'\0',输入字符3个为1组。值得注意的是如果输入字符不是3的倍数,那么最后一组,str[j+1],str[j+2]中依旧会赋值,但是赋的值会被后面switch语句覆盖,所以结果没有影响。            
	{
		encodestr[i]=base64_table[str[j]>>2];	//3*8变4*6的第一个拆分,base64_table前面说了是指针操作,所以可以按字符取。8位一个字符往右移动2位,剩余6位,得出第一个加密下标。
		encodestr[i+1]=base64_table[(str[j]&0x3)<<4 | str[j+1]>>4];	//这里空间折叠可能更好理解,以前的做题经验中 '|' 可以让两边移位同时进行。这里str[j]&0x3取第一个8位的最后2位向前移动4位变成6位中头2位,同时str[j+1]>>4把自身8位中4位向右移动了,那么str[j]8位空间字符剩下2位在前头,str[j+1]8位空间中字符剩下4位在后头,所以合在一起就是6位。
		encodestr[i+2]=base64_table[(str[j+1]&0xf)<<2 | str[j+2]>>6];	//同理,str[j+1]剩下4位向千移动2位变成6位中的头4位,str[j+2]>>6向右移动6位剩余两位,作为6位中的末尾2位。
		encodestr[i+3]=base64_table[str[j+2]&0x3f];	//这里直接与0x3f取6位,不能用<<2,我也暂时不懂                                
	}	
	switch(str_len%3)		//用switch给不足3的最后一组赋值'=',来覆盖前面str[j+1]和str[j+2]生成的但是不改存在的字符。
	{
		case 1:
			encodestr[i-2]='=';
			encodestr[i-1]='=';
			break;
		case 2:
			encodestr[i-1]='=';
			break;
	}
	return encodestr;
 }

char *base64_decode(char *str)//接收要解密的字符串
{

    int table[]={0,0,0,0,0,0,0,0,0,0,0,0,	//根据base64表,以字符找到对应的十进制数据 ,这里是int类型,移位的时候要转换成char地址。
    		 0,0,0,0,0,0,0,0,0,0,0,0,
    		 0,0,0,0,0,0,0,0,0,0,0,0,
    		 0,0,0,0,0,0,0,62,0,0,0,
    		 63,52,53,54,55,56,57,58,
    		 59,60,61,0,0,0,0,0,0,0,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,0,0,0,0,0,0,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
    	       };

	int len;
	int str_len;
	char *decodestr;
	int i,j;
	len=strlen(str);	//获取密文长度,必然是4的倍数。
	if(strstr(str,"=="))	//strstr函数获取字符串位置,要去除掉补加的'='的个数才是真正加密后的密文
		str_len=len/4*3-2;		
	else if (strstr(str,"="))
		str_len=len/4*3-1;
	else
		str_len=len/4*3;
	decodestr=malloc(sizeof(char)*str_len+1);//因为C语言不像python一样可以直接+=来添加字符串,所以要用列表,这里是为列表赋予足够的加密字符空间,加1是因为后面的'\0'结束符。
	decodestr[str_len]='\0';		//写入结束符,那么后面'='不用用全0替换了,因为'='高2位是0,不会影响。
	for(i=0,j=0;i<len-1;j+=3,i+=4)//开始4*6变3*8的拆分循环,i<len-1是因为最后一个字符是'\0',输入字符4个为1组。值得注意的是就算最后有补位用的'=',但是由于'='高2位为0,而且前面有decodestr[str_len]='\0';的截断,所以'='最后不会影响解密结果。        
	{
		decodestr[j]=((char)table[str[i]]) << 2 | ((char)table[str[i+1]]) >> 4;	//4*6变3*8的第一个拆分,table是int地址,所以要转换为char地址才可以按字符取。按4位一组,str[i]的00xxxxxx的8位地址把前面2位0移掉,剩6位。str[i+1]的00xxxxxx后4位移动掉,剩下00xx,实质上是除去前面00只剩下xx两个字符,总共8个字符。           
		decodestr[j+1]=((char)table[str[i+1]]) << 4 | ((char)table[str[i+2]]) >> 2;	//按4位一组,str[i+1]的00xxxxxx的8位地址把前面4位移掉,剩4位。str[i+2]的00xxxxxx后2位移动掉,剩下00xxxx,实质上是除去前面00只剩下xxxx4个字符,总共8个字符。           
		decodestr[j+2]=((char)table[str[i+2]]) << 6 | ((char)table[str[i+3]]);	//按4位一组,str[i+2]的00xxxxxx的8位地址把前面6位移掉,剩2位。str[i+3]的00xxxxxx全部保留,实质上是除去前面00只剩下xxxxxx6个字符,总共8个字符。           
	}
	return decodestr;
}




int main(int argc,char **argv)
{
	char *buff;	//接受加密或解密后的指向字符串的地址
	if(strstr(argv[1],"-d"))	//-d参数作为分隔
	{
		buff=base64_decode(argv[2]);
		printf("%s",buff);
	}
	else
	{
		buff=base64_encode(argv[1]);
		printf("%s",buff);
	}
	return 0;
}

.
.
用法:

gcc -o test 1.c
./test 1234567
/test -d MTIzNDU2Nw==

.
.

base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"			#准备好base64的基表
def encryption(inputstring):		#定义加密函数
	ascii=['{:0>8}'.format(str(bin(ord(i))).replace('0b','')) for i in inputstring]	#把每个输入字符保证8位一个,才能3*8变4*6。
#{:0>8}是右对齐8位然后左边补0,因为python是自己判断数据大小类型的,所以必须强制满足8位。bin转化二进制会带0b前缀,所以要用replace('0b','')去掉。
	encrystr='' #while外的变量,返回base64加密后的字符串
	equalnumber=0 #while外的变量,记录拆分后不足4的倍数时需要补齐的等号个数
	while ascii:
		subascii=ascii[:3] #用一个子列表subascii每次取输入的三位进行操作,前面操作后每位都是8位
		while len(subascii)<3:   #这里其实是最后一段截取中才会用上的,不满足3位时要单独取出,记录equalnumber数量用于后期补'='号,然后补齐8位的0免得干扰后面3*8拆分成4*6
			equalnumber+=1		#计算要补‘=’的个数
			subascii+=['0'*8]	#补8个0来填充够3的倍数,这然后面就不会出错。			
		substring=''.join(subascii)#用substring合并subascii的3个8位,准备进行拆分操作
		encrystringlist=[substring[x:x+6] for x in [0,6,12,18]]   #开始进行3*8变4*6的拆分,每次拆分一组24位。
		encrystringlist=[int(x,2) for x in encrystringlist]		#把前面拆分的6位一组转成10进制,就不用进行位数补齐操作了,这是用来后面对应base64基表的下标。
		if equalnumber:
			encrystringlist=encrystringlist[0:4-equalnumber]	#如果前面不足3字符补了0,比如2个8位字符16位,拆分后就要用3个6位共18位,所以有效位是4-equalnumber
		encrystr+=''.join(base64[x] for x in encrystringlist)	#这里encrystringlist已经在前面拆分成4*6且转换成10进制了,所以对应基表的下标。
		ascii=ascii[3:]		#每次向后取3个列表元素,对应while循环条件
	encrystr+='='*equalnumber		#因为前面encrystringlist[0:4-equalnumber]去掉了补0位,所以这里最后补齐'='号
	return encrystr

def decryption(inputstring):
	ascii=['{0:0>6}'.format(str(bin(base64.index(i))).replace('0b',''))for i in inputstring if i!='=']#从加密字符中取除补位'='之外加密字符,即6位生成的base64基表下标的数,按6位一组排列,准备拆分
	decrystr=''#准备while外的解密后的字符
	equalnumber=inputstring.count('=')#这里计数补位的'='号的个数,后面不够8位时会根据'='号补加位数。
	while ascii:	
		subascii=ascii[:4]#取加密字符的4个6位一组共24位准备拆分合并成3*8
		substring=''.join(subascii)#先连成一串24位
		if len(substring)%8!=0:
			substring=substring[0:-1*equalnumber*2] 
#截取到倒数第equalnumber*2个元素。对不足8位的组补位,因为加密时1个8位要来2个6位,两个'='号,截取到8位就是倒数第4位1*2*2。2个8位要3个6位,要一个'='号,截取到16位就是倒数第2位1*1*2。			
		decrystringlist=[substring[x:x+8] for x in [0,8,16]]#开始进行4*6变3*8的拆分,每次拆分4个6位一组24位。
		decrystringlist=[int(x,2) for x in decrystringlist if x]#把前面拆分的8位一组转成10进制,用来对应十进制ASCII码,if x功能不清楚,但不可缺少,应该是要排除空格吧。
		decrystr+=''.join([chr(x) for x in decrystringlist])#这里decrystringlist已经在前面拆分成3*8且转换成10进制了,现在转换成ASCII码。
		ascii=ascii[4:]#每次向后取4个列表元素,对应while循环条件
	return decrystr

if __name__=="__main__":
	print(encryption('a'))
	print(decryption('YQ=='))

.
.
.
.
总结:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值