手把手教你如何實現一個簡單的數據加密算法


0. 前言

之前寫過一篇關於MD5摘要算法的文章,很多老鐵說能否出一篇關於加密的文章嗎?

C語言實現MD5,竟如此簡單!

一口君的字典里沒有"不行"這兩個字!必須安排!

關於加密的一些基本概念,大家可以參考下面這一篇文章:
公鑰密碼學簡介

本文,一口君帶着大家自己實現一個簡單但也很實用的加密方法,

讓大家了解實際項目開發中數據加密的流程。

一、一種常見的網絡通信的加密流程

關於加密的算法很多,實際實現過程千差萬別,

下圖是一個常見的網絡通信加密的應用場景。

密碼機的一些說明:

  • 客戶端服務器端都可以設置密碼機(可以是軟件、也可以是一個硬件,只要能夠產生密鑰即可)
  • keygen和同步碼都會影響到密碼機生成的密鑰序列
  • 密碼機在keygen和同步碼相同的情況下,會產生相同的密鑰序列,加解密雙方需要記住產生密鑰的順序,解密多少數據就申請多少密鑰

    如上圖所示,基於C/S架構的服務器和客戶端通信模型,

下面以客戶端如果要發送一段加密的密文給服務器,C/S需要交互的流程。

1 服務器端發送密鑰密文

  • 首先服務器端、客戶端都保存了一個默認的密鑰
  • 服務器端隨機生成密鑰keygen,並使用該默認密鑰對keygen加密,生成密鑰密文
  • 客戶端可以通過命令定期請求該密鑰密文或者服務器定時下發
  • 客戶端收到密鑰密文后,也可以通過默認密鑰進行解密得到明文的keygen

2. 客戶端對數據加密

  • 客戶端在發送數據之前,首先生成一個同步碼
  • 將同步碼和keygen設置給密碼機,然后向密碼機申請一定長度的密鑰
  • 將明文和密鑰通過一定的算法進行加密(通常是異或),生成數據密文

3. 客戶端發送同步碼和數據密文

  • 客戶端將數據密文和同步碼明文一起發送給服務器
  • 服務器提取出同步碼

4. 服務器端接收數據並解密

  • 服務器將keygen和同步碼設置給密碼機,同時申請一定數量的密鑰
  • 服務器根據密鑰對密文進行解密,即得到對應的明文

因為服務器和客戶端此時都使用了相同的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 

可見輸入不同的種子就會產生不同的序列。

函數原型如下:


本例原理比較簡單,沒有考慮太復雜的應用(比如多路密鑰的管理)和數據安全性,

只闡述加解密的流程,僅作為學習理解加解密流程用,此種加密算法屬於對稱加密,相對比較簡單,還是比較容易破解。

目前市場上都是由專業的公司和團隊實現加解密功能。

一口君之前曾寫過聊天室的一個小項目,

從0實現基於Linux socket聊天室

后面一口君會基於該加密機制,將聊天室所有客戶端與服務器所有交互數據進行加密處理,請大家持續關注:一口Linux。

本文完整代碼下載地址:
鏈接:https://pan.baidu.com/s/1VvGNlNGEUWWZHQZ1_gYU7A 提取碼:o9se

后台回復:數據加密,即可獲得全部源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM