物联网实战--驱动篇之(五)TEA和AES加密算法

目录

一、前言

二、TEA算法

三、AES算法

四、加解密测试

五、安全性保障


一、前言

物联网的安全性是经常被提及的一个点,如果你的设备之间通讯没有加密的话,那么攻击者很容易就能获取并解析出报文的协议,从而根据攻击者的需要进行设备操控和敏感信息获取。举个例子,现在有很多的WiFi插座,连接WIFI后用户就能通过手机去控制插座开关,如果设计者对安全不重视,所有报文都是明文,那么攻击者只要设局让用户连在同一个局域网WiFi,获取一些身份信息,即可自己组合相关报文对插座进行控制了,是一件比较危险的事情。

对于设备端,处理器性能有好有差的,所以对于加密算法要根据实际情况去选配,在这里,我们主要介绍TEA和AES两种加密算法,具体的算法原理属于加密学的内容,还是挺难懂的,有兴趣可以自己深入研究,我们这里主要讲下如何封装和使用这两种加密算法。

加解密的流程如下图所示,我们这里都是对称加密,加解密用的同一个密钥,没什么太复杂的流程。

二、TEA算法

TEA是一个非常轻量级的加解密算法,效率高、强度好,很多游戏类有实时性要求的都是用TEA算法,QQ好像也是,它的基本介绍百科里有TEA加密算法_百度百科。对于我们使用者而言,主要有几个点注意下就行了。

1、TEA采用对称加密,数据是8字节一组,密钥是16字节一组,所以对用户层的数据有要求,数据需要8字节对齐,不够的自己补齐再输入;密码要16字节。

2、TEA算法的加密强度跟算法本身关系不大,主要还是加密轮次,建议是32轮,所以驱动库里就写死了,加解密算法轮次要对应。

下面先展现每组的加密单元,代码如下所示:

cpp 复制代码
/*		
================================================================================
描述 :  TEA加密单元
输入 : 
输出 : 
================================================================================
*/
void EncryptTEA(u32 *firstChunk, u32 *secondChunk, u32* key)
{
	u32 y = *firstChunk;
	u32 z = *secondChunk;
	u32 sum = 0;
	u32 delta = 0x9e3779b9;

	for (int i = 0; i < 32; i++)//32轮运算(需要对应下面的解密核心函数的轮数一样)
	{
		sum += delta;
		y += ((z << 4) + key[0]) ^ (z + sum) ^ ((z >> 5) + key[1]);
		z += ((y << 4) + key[2]) ^ (y + sum) ^ ((y >> 5) + key[3]);
	}

	*firstChunk = y;
	*secondChunk = z;
 }

/*		
================================================================================
描述 : TEA解密单元
输入 : 
输出 : 
================================================================================
*/
void DecryptTEA(u32 *firstChunk, u32 *secondChunk, u32* key)
{
	u32  sum = 0;
	u32  y = *firstChunk;
	u32  z = *secondChunk;
	u32  delta = 0x9e3779b9;

	sum = delta << 5; //32轮运算,所以是2的5次方;16轮运算,所以是2的4次方;8轮运算,所以是2的3次方

	for (int i = 0; i < 32; i++) //32轮运算
	{
		z -= (y << 4) + key[2] ^ y + sum ^ (y >> 5) + key[3];
		y -= (z << 4) + key[0] ^ z + sum ^ (z >> 5) + key[1];
		sum -= delta;
	}

	*firstChunk = y;
	*secondChunk = z;
}

具体核心函数的内部原理我们就不考究了,简单的观察就是4字节+4字节数据与16字节的密码做相关运算。

接下来看下如何调用这个核心加解密函数,具体代码如下:

cpp 复制代码
/*		
================================================================================
描述 :TEA数据加密函数
输入 : buff的长度必须是8的整数倍
输出 : 
================================================================================
*/
u16 tea_encrypt_buff(u8 *buff, u16 len, u32* key)
{
	u8 *p = buff; 
	u16 i,counts;

	if(len%8!=0)
	{
		printf("Encrypt buff len err!\n");
		return 0;
	}
	counts=len/8;	
	for(i=0;i<counts;i++)
	{
		EncryptTEA((u32 *)p, (u32 *)(p + 4), key);
		p+=8;
	}
	return len;
}

/*		
================================================================================
描述 : TEA数据解密函数
输入 : buff的长度必须是8的整数倍
输出 : 
================================================================================
*/
u16 tea_decrypt_buff(u8 *buff, u16 len, u32* key)  
{
	u8 *p = buff; 
	u16 i,counts;

	if(len%8!=0)
	{
		printf("Decryp buff len err!\n");
		return 0;
	}	
	counts=len/8;	
	for(i=0;i<counts;i++)
	{
		DecryptTEA((u32 *)p, (u32 *)(p + 4), key);
		p+=8;
	}
	
	return len;
}

这是自己封装的,主要是检测输入的数据长度有没有8字节对齐,然后调用加解密函数对每个单元的数据一次操作,由于是指针传递,所以明文和密文都是在同一个缓冲区内。

三、AES算法

AES算法是当今使用最多的对称加密算法了,效率高、安全性好,它的实现比较复杂,我们用的是mbedtls库,把其中的AES相关部分拿出来,因为整个库对于单片机来讲着实有点大了GitHub - Mbed-TLS/mbedtls: An open source, portable, easy to use, readable and flexible TLS library, and reference implementation of the PSA Cryptography API. Releases are on a varying cadence, typically around 3 - 6 months between releases.

AES内部还分了5中加密模式,具体看这里介绍,我们选择的是cbc模式,密码16字节,其它模式自己也可以尝试。AES五种加密模式(CBC、ECB、CTR、OCF、CFB) - 知乎

下面具体看下驱动库封装后的程序,代码如下:

cpp 复制代码
/*		
================================================================================
描述 :AES-CBC模式加密
输入 : 
输出 : 
================================================================================
*/
int aes_encrypt_buff(u8 *in_buff, u16 in_len,u8 *out_buff, u16 out_size,u8 *passwd)
{
	static mbedtls_aes_context aes_ctx;
	u16 loop_cnts=0;//循环加密次数
	u8 temp_buff[20]={0};
	u8 iv[17]={0},key[17]={0};
	loop_cnts=in_len/16;
	if(in_len%16>0)
		loop_cnts++;

	if(loop_cnts*16>out_size)
		return 0;

	if(strlen((char*)passwd)>16)
	{
		memcpy(key, passwd, 16);
	}
	else
	{
		strcpy((char*)key, (char*)passwd);
	}
	mbedtls_aes_init(&aes_ctx);
	mbedtls_aes_setkey_enc(&aes_ctx, key, 128);
	memset(iv,'0',sizeof(iv));
	for(int i=0;i<loop_cnts;i++)
	{
		if(i==loop_cnts-1 && in_len%16>0)//最后一组
		{
			memset(temp_buff, 0, sizeof(temp_buff));
			memcpy(temp_buff, &in_buff[i*16], in_len%16);//用0填充
		}
		else
		{
			memcpy(temp_buff, &in_buff[i*16], 16);
		}
		mbedtls_aes_crypt_cbc(&aes_ctx,  MBEDTLS_AES_ENCRYPT, 16, iv, temp_buff,  &out_buff[i*16]);
	}

	return loop_cnts*16;
}

/*		
================================================================================
描述 :AES-CBC模式解密
输入 : 
输出 : 
================================================================================
*/
int aes_decrypt_buff(u8 *in_buff, u16 in_len,u8 *out_buff, u16 out_size,u8 *passwd)
{
	mbedtls_aes_context aes_ctx;
	u16 loop_cnts=0;//循环加密次数
	u8 temp_buff[16]={0};
	u8 iv[16]={0},key[17]={0};
	loop_cnts=in_len/16;
	if(in_len%16>0)
		return 0;   //密文长度必须是16的整数倍

	if(loop_cnts*16>out_size)
		return 0;

	if(strlen((char*)passwd)>16)
	{
		memcpy(key, passwd, 16);
	}
	else
	{
		strcpy((char*)key, (char*)passwd);
	}
	mbedtls_aes_init(&aes_ctx);
	mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
	memset(iv,'0',sizeof(iv));
	for(int i=0;i<loop_cnts;i++)
	{
		memcpy(temp_buff, &in_buff[i*16], 16);
		mbedtls_aes_crypt_cbc(&aes_ctx,  MBEDTLS_AES_DECRYPT, 16, iv, temp_buff,  &out_buff[i*16]);
	}
	return loop_cnts*16;
}

CBC模式需要初始化向量,这里全部初始化为'0',核心还是调用mbedtls的库函数,代码自行阅读,接下来做一些测试,看下如何使用。

四、加解密测试

测试环境如下所示:

如果采用的是净化器项目的工程代码,那么user_opt.h和rtconfig.h文件参数修改下,不然RAM不够用,任务无法运行,AES算法有点吃内存,对于小身板来讲比较够呛。

下面是测试代码,放在user_app.c文件里,代码如下:

cpp 复制代码
  
  u8 in_buff[32]={"0123456789ABCDEF0123456789ABCDEF"};
  u8 out_buff[32]={0};
  u8 passwd[16]={"0123456789123456"};
  
  //TEA加解密测试
  printf("*****start tea test!\n");
  printf("000 in_buff=%s\n", in_buff); 
  tea_encrypt_buff(in_buff, 32, (u32*)passwd);//TEA加密
  printf_hex("out_buff=", in_buff, 32);//打印密文

  tea_decrypt_buff(in_buff, 32, (u32*)passwd);//TEA解密
  
  printf("111 in_buff=%s\n", in_buff);//打印解密明文  
  
  //AES加解密测试
  printf("\n*****start aes test!\n");
  printf("000 in_buff=%s\n", in_buff); 
  aes_encrypt_buff(in_buff, 32, out_buff, 32, passwd);//AES加密
  printf_hex("out_buff=", out_buff, 32);//打印密文
  
  memset(in_buff, 0, 32);//清空明文区
  aes_decrypt_buff(out_buff, 32, in_buff, 32, passwd);//AES解密
  
  printf("222 in_buff=%s\n", in_buff);   

测试结果如下图所示,密文用16进制的方式打印,不然是乱码:

从结果上看,加解密算法没什么问题,速度也还可以,不过AES算法对于STM32F103C8T6可能还是有点大了,芯片的RAM是20KB,单纯AES文件就要用掉10KB左右,所以对于前端小型设备,可能还是TEA算法比较合适,这个自行选择。

五、安全性保障

现在回到物联网本身,有哪些我们可以采用加密传输呢?像购买的从机设备一般是没办法的,比如485温湿度传感器,这种厂家已经固定程序了,不会为你去做适配的,不过这一类传感器也不必要加密通讯了,因为它是有线局部传输,物理环境本身比较安全。备端的加密一般放在无线组网方面和主机与服务器通讯方面。

首先无线组网数据容易被截获,如果内容是加密的,对方破解需要代价和时间,如果你在内容里加上时间戳等信息,可以有效防止重放攻击,以后会讲的LoRa自组网就会用到加密算法了。

主机跟服务器方面就不用多说了,这部分如果没有加密的话很容易被攻击,因为数据一般是发往互联网的,攻击者的操作手段太多了,我们只能尽可能得做好数据加密,防止一些常规手段的攻击。

安全性不单单是加密算法的事,更重要的是密钥的存放,如果密钥很容易就被获取了,那么跟没加密是一样的。对于单片机设备,如果有一定价值,攻击者可以通过非正常手段读取单片机内部flash的所有内容,如果你的密钥是明文写在程序内的,对方很快就能获悉了,所以一个高可靠性产品的代码写起来确实不容易,至于要如何防止,只能在后续项目实操中融入了。

代码链接:https://download.csdn.net/download/ypp240124016/89105829

本项目的交流QQ群:701889554

相关推荐
糖糖单片机设计9 小时前
硬件开发_基于STM32单片机的电脑底座系统
stm32·单片机·嵌入式硬件·物联网·51单片机
蓝蜂物联网1 天前
边缘计算网关赋能智慧农业:物联网边缘计算的创新应用与实践
人工智能·物联网·边缘计算
TDengine (老段)1 天前
TDengine 转化类函数 TO_CHAR 用户手册
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
蓝蜂物联网1 天前
边缘计算网关与 EMCP 物联网云平台:无缝协作,共筑智能生态
人工智能·物联网·边缘计算
远创智控研发五部1 天前
C200H以太网通道服务监控、人机交互与驱动
物联网·数据采集·以太网模块·工业自动化·欧姆龙plc
绿蕉1 天前
中国5G RedCap基站开通情况及2025年全年计划
物联网·5g redcap·蜂窝通讯
熬夜的猪仔2 天前
第五章 Freertos物联网实战 微信小程序篇
物联网·freertos·微信小程序开发
熬夜的猪仔3 天前
第四章 Freertos物联网实战DHT11温湿度模块
物联网·freertos·dht11温湿度模块
WIZnet3 天前
第二十七章 W55MH32 Interrupt示例
物联网·以太网·wiznet·中断·高性能以太网单片机·w55mh32·toe
时序数据说3 天前
时序数据库主流产品概览
大数据·数据库·物联网·时序数据库·iotdb