手把手教你如何实现一个简单的数据加解密算法

380次阅读  |  发布于3年以前

本文, 带着大家自己实现一个简单但也很实用的加密方法,

让大家了解实际项目开发中数据加密的流程。

一、一种常见的网络通信的加密流程

关于加密的算法很多,实际实现过程千差万别,

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

密码机的一些说明:

下面以客户端如果要发送一段加密的密文给服务器,C/S需要交互的流程。

1 服务器端发送密钥密文

2. 客户端对数据加密

3. 客户端发送同步码和数据密文

4. 服务器端接收数据并解密

因为服务器和客户端此时都使用了相同的keygen,和同步码,所以双方申请的密钥序列一定是一样的。

二、函数实现

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

1. 申请加密密钥函数request_key

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

2. 设置密钥序列函数set_keygen

void set_keygen(int key)
功能:
 向密码机设置keygen,设置后会影响产生的随机密钥序列
参数:
 key:密钥
返回值:
 无

3. 产生随机数born_seed

int born_seed(int sync,int key)
功能:
 根据同步码和keygen生成随机密钥种子
参数:
    sync:同步码 
 key:密钥
返回值:
 种子

4. 重置keygen reset_keygen()

void reset_keygen()
功能:
 重置keygen,会影响生成的随机数序列

三、测试代码实例

最终文件如下:

key.c  key.h  main.c

示例1 检测产生的随机序列

int main(int argc, char *argv[])
{
 int i;
 unsigned int len;
 int j, r, key_num;
 unsigned int sync = 0;
 unsigned char key[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]
a5 52 c8 14 5d f7 46 5b 89 42 
密钥10-19: ----[10]
38 69 6f a6 08 d2 69 39 cd 29 

--------------采用keygen=1234 同步码=0 产生密文----------------
密钥0-9: ----[10]
0e 83 0b 73 ec f5 4b 4a 74 35 
密钥10-19: ----[10]
e7 f1 06 41 c8 6b aa df 0c 3d 

可以看到采用不同的keygen产生的随机序列是不一样的。

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

示例2 用默认keygen,加解密

char data0[10]={
 0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0x10,
};
int main(int argc, char *argv[])
{
 int i;
 unsigned int len;
 int j, r, key_num;
 unsigned int sync = 0;
 unsigned char key[MAX_KEY_REQUEST];
 char buf[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]
01 02 03 04 05 06 07 08 09 10 
密钥: ----[10]
a5 52 c8 14 5d f7 46 5b 89 42 

密文: ----[10]
a4 50 cb 10 58 f1 41 53 80 52 

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

明文: ----[10]
01 02 03 04 05 06 07 08 09 10 

示例3 用不同的keygen和同步码加解密

int main(int argc, char *argv[])
{
 int i;
 unsigned int len;
 int j, r, key_num;
 unsigned int sync = 0;
 unsigned char key[MAX_KEY_REQUEST];
 char buf[120]={0};
 unsigned int mykeygen;


 if (argc != 4) {
  fprintf(stderr, "Usage: %s <seed> <key num> <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]
01 02 03 04 05 06 07 08 09 10 
密钥: ----[10]
53 00 29 cd 27 eb cc 80 1a d7 

密文: ----[10]
52 02 2a c9 22 ed cb 88 13 c7 

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

明文: ----[10]
01 02 03 04 05 06 07 08 09 10 

可见我们的确实现了数据的加密和解密。

四、数据加密的实际使用

假定我们使用上述实例代码,把对应的功能移植到C/S两端,

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

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

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

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

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

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

五、 原理

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

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

#include <stdlib.h>

int rand(void);

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

#include <stdio.h>

main()
{
 int i = 0;

 for(i=0;i<10;i++)
 {
  printf("%d ",rand());
 }
 putchar('\n');
}

运行结果如下:

peng@peng-virtual-machine:/mnt/hgfs/peng/rand/code$ ./a.out 
1804289383 846930886 1681692777 1714636915 1957747793 424238335 719885386 1649760492 596516649 1189641421 
peng@peng-virtual-machine:/mnt/hgfs/peng/rand/code$ ./a.out 
1804289383 846930886 1681692777 1714636915 1957747793 424238335 719885386 1649760492 596516649 1189641421 

要想每次都产生不一样的随机序列应该怎么办呢?需要借助srand()函数

void srand(unsigned int seed);

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

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

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

#include <stdio.h>

main()
{
 int i = 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 
1629905861 708017477 1225010071 14444113 324837614 2112273117 1166384513 1539134273 1883039818 779189906 
1383711924 882432674 1555165704 1334863495 1474679554 676796645 154721979 534868285 1892754119 100411878 

可见输入不同的种子就会产生不同的序列。

函数原型如下:

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

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

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

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8