因工作对接需求,需要解析出对方通过http发送过来的字符串(3des+base64加密),而对端(可以理解为客户端)关于这块内容使用的纯jave语言编写,并且调用的是原生的jdk的包,具体见:https://github.com/anylogic/alogic/blob/master/alogiccommon/src/main/java/com/anysoft/util/code/coder/DES3.java 。
而本端(可以理解为服务端)是使用的nginx+luajit搭建的http服务端。而luajit只能调用通过c语言编写的动态库(通过ffi的方式。具体的细节广大网友可以自行查阅相关资料,这块不作为本文的重点),于是,c语言版的3des+base64加解密源码就显得迫不及待了!
在网上搜罗大量的3des+base64的相关源码,找到了大量的版本(包括c/c++),没有一份能准确无误的解析出对端发送过来的字符串。终于在慢慢探索的道路上,找到了一份能基本解析的c++的版本。为什么说是基本解析,因为里面包含了一些小的瑕疵,真正用在项目上还存在一定的差距。那么,目前摆在面前的有两个问题:
1.将c++版翻译成c版。
源c++版地址:
不做过多的诉说,有一定c/c++基础的同学都能做到。
只是在字符串转16进制和16进制转字符串在这里贴一下代码。
c++是通过流的方式去处理的,与c还是有很大的不同。
//字符串转化为16进制
unsigned char* str_to_hex(const char *str)
{
int i;
unsigned char *buff = NULL;
int nLen = strlen(str);
int size = sizeof(unsigned char *) * nLen / 2;
buff = (unsigned char*) malloc(size);
memset((void*) buff, 0, size);
char szTmp[3];
for(i = 0;i<nLen;i++)
{
sprintf(szTmp,"%02x",(unsigned char)str[i]);
memcpy(&buff[i*2],szTmp,2);
}
return buff;
}
//16进制转化为字符串
unsigned char * hex_to_str(const char *src)
{
int i;
unsigned char hbyte,lbyte;
int len = strlen(src);
unsigned char *buff = NULL;
buff = (unsigned char *)malloc(len/2);
for (i=0;i<len;i+=2)
{
hbyte = toupper(src[i]);
lbyte = toupper(src[i+1]);
if (hbyte > 0x39) {
hbyte -= 0x37;
} else {
hbyte -= 0x30;
}
if (lbyte > 0x39) {
lbyte -= 0x37;
} else {
lbyte -= 0x30;
}
buff[i/2] = (hbyte << 4)|lbyte;
}
return buff;
}
2.解决里面的瑕疵。
2.1:不能正确解析出对端发送过来的数据。
走查源码发现了问题了所在。在数组清除的地方存在问题,当然这个要感谢另一个博主,在他的博文中: 有提到:
3des-ecb加密方式:
1.24位密钥,不足24位的右补0x00;
2.加密内容8位补齐,补齐方式为:少1位补一个0x01,少2位补两个0x02,…
3.本身已8位对齐的,后面补八个0x08。
很明显下面c++版清零的补齐处理方式与上面的观点相悖
/************************************************************************
*功能:数组清空
************************************************************************/
void Des::clearBuff(char *buff, int length) {
for (int i = 0; i < length; i++) {
buff[i] = '\0';
}
}
2.修改后的c版
//判断是否是8位对齐。补齐方式为:少1位补一个0x01,少2位补两个0x02,…
char ch = 0; // 不过本人觉得ch初始化为0的方式有些不妥,是否要初始化为8,这个要留给博友们去验证!
if (strlen % 8 == 0) {
block = strlen / 8;
} else {
block = strlen / 8 + 1;
ch = 8 - strlen % 8;
}
/************************************************************************
*功能:数组清空
************************************************************************/
void clearBuff(char *buff, int length, char ch) {
int i;
for (i = 0; i < length; i++) {
buff[i] = ch;
}
}
2.2:解密后数据长度存在问题,每次解析出来的数据长度总是为8的倍数。
那么源数据不是8的倍数的时候就会出现问题。默认会填充数据进去。后打印填充数据,发现填充的数据为数字,并且该数字等于填充长度,貌似与加密的处理方式一样。但后面通过大量的实例发现存在小部分的情况不是这样,于是就只能粗暴的处理了,在每次返回解密数据的时候做下面的处理
int s = strlen(out) - 1;
for(s;s > 0;s--) {
if (out[s] < 8) {
out[s] = '\0';
}
}
经过上面的处理后,问题得到解决。