用c语言实现一个简单的数据加解密算法
来源:一口Linux 发布时间:2024-08-12 分享至微信


新上映的电影《解密》看的有点上头。
整体来说,这是一部很不错的电影。
156分钟的电影,剧情紧凑,信息量很大,
除去最后2分钟,全程几乎无尿点,
本部没有启用流量明星,云集了众多老戏骨,演技绝对在线。
有一些情节致敬了《盗梦空间》梦境(小李子是拧陀螺)、《美丽人生》纳什(双重性格)、《模仿游戏》图灵(用计算机破解英格玛),建议观影前先不要看这几部电影。
如果非要给本电影找瑕疵的话,
就是电影的最后2分钟,强烈建议不要看,
看到陈导出镜,
以为成才退役后转行干记者了,
陈导的字还不如我女儿一年级的时候写得好。
还有彩蛋也强烈建议别看,别看,别看,
真的很下头,画蛇添足。


言归正传,电影中讲到了数据加密,

彭老师之前玩过对称/非对称加密,

于是我借助linux的库函数radom(),实现了一个简单的加密算法demo,

并且我发给了专业搞加密的朋友,得到了对方的认可,

当时他还强烈要求我加入他们的团队(* ̄︶ ̄)。

下面就带着大家了解我是如何实现这个简单但却很实用的加密方法。

0. 前言
相关文章

《C语言实现MD5,竟如此简单!》

《公钥密码学简介》



一、一种常见的网络通信的加密流程
关于加密的算法很多,实际实现过程千差万别,

下图是一个常见的网络通信加密的应用场景。

密码机的一些说明:

客户端服务器端都可以设置密码机(可以是软件、也可以是一个硬件,可以在本地也可以在某个服务器上,只要能够产生密钥即可)
keygen和同步码都会影响到密码机生成的密钥序列
密码机在keygen和同步码相同的情况下,会产生相同的密钥序列,加解密双方需要记住产生密钥的顺序,解密多少数据就申请多少密钥
如上图所示,基于C/S架构的服务器和客户端通信模型,
下面以客户端如果要发送一段加密的密文给服务器,C/S需要交互的流程。

1 服务器端发送密钥密文
首先服务器端、客户端都保存了一个默认的密钥
服务器端随机生成密钥keygen,并使用该默认密钥对keygen加密,生成密钥密文
客户端可以通过命令定期请求该密钥密文或者服务器定时下发
客户端收到密钥密文后,也可以通过默认密钥进行解密得到明文的keygen
2. 客户端对数据加密
客户端在发送数据之前,首先生成一个同步码
将同步码和keygen设置给密码机,然后向密码机申请一定长度的密钥
将明文和密钥通过一定的算法进行加密(通常是异或),生成数据密文
3. 客户端发送同步码和数据密文
客户端将数据密文和同步码明文一起发送给服务器
服务器提取出同步码
4. 服务器端接收数据并解密
服务器将keygen和同步码设置给密码机,同时申请一定数量的密钥
服务器根据密钥对密文进行解密,即得到对应的明文
因为服务器和客户端此时都使用了相同的keygen,和同步码,所以双方申请的密钥序列一定是一样的。

二、函数实现
下面是一口君实现的加密算法的一些函数原型以及功能说明,这些函数基本实现了第一节的功能。

1. 申请加密密钥函数request_key
intrequest_key(intsync,intkey_num,charkey[])
功能:
向密码机申请一定数量的用于加密数据的密钥,如果不设置新的keygen,那么生成的密码会顺序产生下去,每次申请密钥都会记录上次生成的密钥的偏移,下次在申请的时候,都会从上一位置继续分配密钥
参数:
sync:同步码,密码机依据此同步产生随机序列的密钥
key_num:申请的密钥个数
key:申请的密钥存储的缓存
返回值:
实际返回密钥个数
2. 设置密钥序列函数set_keygen
voidset_keygen(intkey)
功能:
向密码机设置keygen,设置后会影响产生的随机密钥序列
参数:
key:密钥
返回值:

3. 产生随机数born_seed
intborn_seed(intsync,intkey)
功能:
根据同步码和keygen生成随机密钥种子
参数:
sync:同步码
key:密钥
返回值:
种子
4. 重置keygen reset_keygen()
voidreset_keygen()
功能:
重置keygen,会影响生成的随机数序列
三、测试代码实例
最终文件如下:

key.ckey.hmain.c
示例1 检测产生的随机序列
intmain(intargc,char*argv[])
{
inti;
unsignedintlen;
intj,r,key_num;
unsignedintsync=0;
unsignedcharkey[MAX_KEY_REQUEST];


key_num=10;

printf("\n--------------采用默认keygen同步码=0产生密文----------------\n");
reset_keygen();

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);

print_array("密钥0-9:",key,len);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);

print_array("密钥10-19:",key,len);

printf("\n--------------采用keygen=1234同步码=0产生密文----------------\n");
set_keygen(1234);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);

print_array("密钥0-9:",key,len);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);

print_array("密钥10-19:",key,len);
}
执行结果:

--------------采用默认keygen同步码=0产生密文----------------
密钥0-9:----[10]
a552c8145df7465b8942
密钥10-19:----[10]
38696fa608d26939cd29

--------------采用keygen=1234同步码=0产生密文----------------
密钥0-9:----[10]
0e830b73ecf54b4a7435
密钥10-19:----[10]
e7f10641c86baadf0c3d
可以看到采用不同的keygen产生的随机序列是不一样的。

如果设置不同的同步码,仍然序列还会不一样。

示例2 用默认keygen,加解密
chardata0[10]={
0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0x10,
};
intmain(intargc,char*argv[])
{
inti;
unsignedintlen;
intj,r,key_num;
unsignedintsync=0;
unsignedcharkey[MAX_KEY_REQUEST];
charbuf[120]={0};

key_num=10;
printf("\n--------------采用默认keygen开始加密----------------\n");
reset_keygen();
print_array("\n明文:",data0,key_num);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);

print_array("密钥:",key,len);
for(i=0;i<len;i++)
{
buf[i]=data0[i]^key[i];
}
print_array("\n密文:",buf,len);

printf("\n--------------------开始解密--------------------\n");
reset_keygen();

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);


for(i=0;i<len;i++)
{
buf[i]=buf[i]^key[i];
}

print_array("\n明文:",buf,len);
}
测试结果

--------------采用默认keygen开始加密----------------

明文:----[10]
01020304050607080910
密钥:----[10]
a552c8145df7465b8942

密文:----[10]
a450cb1058f141538052

--------------------开始解密--------------------

明文:----[10]
01020304050607080910

示例3 用不同的keygen和同步码加解密
intmain(intargc,char*argv[])
{
inti;
unsignedintlen;
intj,r,key_num;
unsignedintsync=0;
unsignedcharkey[MAX_KEY_REQUEST];
charbuf[120]={0};
unsignedintmykeygen;


if(argc!=4){
fprintf(stderr,"Usage:%s<seed><keynum><keygen>\n",argv[0]);
exit(EXIT_FAILURE);
}

sync=atoi(argv[1]);
key_num=atoi(argv[2]);
mykeygen=atoi(argv[3]);

printf("\n--------------采用自定义的keygen、同步码开始加密----------------\n");
set_keygen(mykeygen);
print_array("\n明文:",data0,key_num);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);
print_array("密钥:",key,len);

for(i=0;i<len;i++)
{
buf[i]=data0[i]^key[i];
}
print_array("\n密文:",buf,len);


printf("\n--------------------开始解密--------------------\n");
set_keygen(mykeygen);

memset(key,0,sizeof(key));
len=request_key(sync,key_num,key);
for(i=0;i<len;i++)
{
buf[i]=buf[i]^key[i];
}
print_array("\n明文:",buf,len);
exit(EXIT_SUCCESS);
}
执行结果如下:

--------------采用自定义的keygen、同步码开始加密----------------

明文:----[10]
01020304050607080910
密钥:----[10]
530029cd27ebcc801ad7

密文:----[10]
52022ac922edcb8813c7

--------------------开始解密--------------------

明文:----[10]
01020304050607080910
可见我们的确实现了数据的加密和解密。

四、数据加密的实际使用
假定我们使用上述实例代码,把对应的功能移植到C/S两端,

那么一次完整的数据加密以及数据的传输参考流程如下:

记住一点,只要双方设置相同的keygen和同步码,那么密码机吐出来的密钥就是相同序列,

客户端发送每发送一个报文,就把自己的明文同步码一起发送给服务器,

服务器根据提前发送给客户端的keygen和同步码就可以实现解密操作,

虽然你可以看到明文的同步码,

但是还需要破解密码机算法、服务器下发的keygen密文。

五、 原理
实现加密算法的主要问题是如何产生随机序列作为密钥。

本例是借用库函数rand() 原型如下:

#include<stdlib.h>

intrand(void);
函数rand() 虽然可以产生随机序列,但是每次产生的序列其实顺序是一样的。

#include<stdio.h>

main()
{
inti=0;

for(i=0;i<10;i++)
{
printf("%d",rand());
}
putchar('\n');
}
运行结果如下:

peng@peng-virtual-machine:/mnt/hgfs/peng/rand/code$./a.out
180428938384693088616816927771714636915195774779342423833571988538616497604925965166491189641421
peng@peng-virtual-machine:/mnt/hgfs/peng/rand/code$./a.out
180428938384693088616816927771714636915195774779342423833571988538616497604925965166491189641421
要想每次都产生不一样的随机序列应该怎么办呢?需要借助srand()函数

voidsrand(unsignedintseed);
只需要通过该函数设置一个种子,那么产生的序列,就会完全不一样,

通常我们用time()返回值作为种子,

在此我们随便写入几个数据,来测试下该函数

#include<stdio.h>

main()
{
inti=0;

srand(111);
for(i=0;i<10;i++)
{
printf("%d",rand());
}
putchar('\n');
srand(1111);
for(i=0;i<10;i++)
{
printf("%d",rand());
}
putchar('\n');
}
执行结果如下:

peng@peng-virtual-machine:/mnt/hgfs/peng/rand/code$./a.out
16299058617080174771225010071144441133248376142112273117116638451315391342731883039818779189906
13837119248824326741555165704133486349514746795546767966451547219795348682851892754119100411878
可见输入不同的种子就会产生不同的序列。

函数原型如下:

本例原理比较简单,没有考虑太复杂的应用(比如多路密钥的管理)和数据安全性,

只阐述加解密的流程,仅作为学习理解加解密流程用,此种加密算法属于对称加密,相对比较简单,还是比较容易破解。

目前市场上都是由专业的公司和团队实现加解密功能。

一口君之前曾写过聊天室的一个小项目,

《聊天室》

从0实现基于Linux socket聊天室-多线程服务器模型-1

从0实现基于Linux socket聊天室-多线程服务器一个很隐晦的错误-2

从0实现基于Linux socket聊天室-实现聊天室的登录、注册功能-3

从0实现基于Linux socket聊天室-增加公聊、私聊-4必看

从0实现基于Linux socket聊天室-增加数据库sqlite功能-5必看

从0实现基于Linux socket聊天室-增加数据加密功能-6

彭老师基于该加密机制,将聊天室所有客户端与服务器所有交互数据进行加密处理,有兴趣的老铁可以下载学习下。

本文完整代码下载地址:

链接:https://pan.baidu.com/s/1VvGNlNGEUWWZHQZ1_gYU7A 提取码:o9se

后台回复:数据加密,即可获得全部源码




一口Linux
关注,回复【1024】海量Linux资料赠送

[ 新闻来源:一口Linux,更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj]
存入云盘 收藏
举报
全部评论

暂无评论哦,快来评论一下吧!