低功耗蓝牙BLE之AES-128加密算法

版权声明:
博主:枫之星雨
声明:本文为博主原创文章,转载请注明原文出处。
博文地址:点击进入
邮箱:[email protected]
QQ号:454086991(申请加好友时请备注”技术交流“)




加密


在连接时,可以对净荷中的数据进行加密,确保数据的机密性,从而抵御攻击者。机密性是指第三方攻击者由于没有加密链路的共享密钥,因此无法拦截、破译或读取消息的原始内容。

 

加密数据包含一个消息完整性校验值,表明该数据包已经过认证。为了验明发送方的有效身份,认证使用共享密钥为已加密的数据计算签名,可防止第三方篡改数据包中的任何内容。通过认证,消息的接收方能够确信收到的数据包来自一个可信设备。

 

加密数据包还包含一个数据包计数器,用来防止重放攻击。重放攻击是这样一种攻击方式:攻击者截取一个既有的消息,随后再次发送该消息以期望收到响应。试想,假如没有重放攻击保护,攻击者有可能扫描到某个设备的大量数据包,然后再次发送这些数据包,其结果可能不堪设想。显然,防止重放攻击是一件非常重要的事情。




AES-128加密算法简介


低功耗蓝牙中的所有加密和认证都基于同一个加密引擎,称为高级加密系统(AES)。AES最初源自美国的一项政府计划,试图寻找未来可用的加密引擎。一直以来,AES被用于许多有线和无线标准,迄今为止安全研究人员还没有找到其算法的弱点。

 

AES可以有多种形式,取决于在给定的时间内能够处理的数据块以及密钥的大小。低功耗蓝牙使用128位的密钥和128位的数据块。也就是说,所有密钥的长度均为128位,每次加密生成的密文长度为16个字节。

 

AES加密块非常简单,它包含两个输入和一个输出。两个输入分别为128位的密钥值和128位的纯文本数据块,输出则为128位的加密数据块。密钥和纯文本在使用上有一些不同;纯文本可以直接为加密块使用,但密钥必须经过处理后才能使用。可见,更有效的方法是只设立一个密钥,用于不同的纯文本块以进行快速加密,而非使用不同的密钥为每个块加密。

 

在低功耗蓝牙里,AES加密引擎被用于下列四个基本功能:

1.加密净荷数据

2.计算消息完整性校验值

3.数据签名

4.生成私有地址

数据签名在安全管理器中定义,生成私有地址在通用访问规范中定义。

 

注意,本文主要是为了讲解AES-128算法在BLE设备和安卓手机之间通信时使用的过程,所以并没有去深入研究讲解AES算法。如果有对该算法感兴趣的,可以去网上搜索相关资料深入了解一下。




AES-128加解密方法源码


我们的实验案例是BLE设备端(从机)发送加密的数据给安卓手机(主机)并解密出原始有效数据,以及安卓手机(主机)发送加密的数据给BLE设备端(从机)并解密出原始有效数据。通信过程不便于演示,所以我们换种方式来呈现我们的实现源码:同样的原始有效数据,同样的加解密Key,分别在设备端和Java测试环境中对原始有效数据进行加密然后解密,通过看设备端和Java测试环境中加密之后的数据是否一致以及解密之后的数据是否一致,来判断我们的数据加密传输能否成功,如果加密之后数据一致并且解密之后的数据也一致,那加密传输就可以成功实现。


1.BLE设备端测试程序。

(1)测试环境:

开发环境:IAR8.20版本,Windows XP系统

测试设备:CC2541/CC2540开发板

测试例程:1.4.0协议栈中simpleBLEPeripheral例程

(2)实现源码:

/****************************************************************
* 名    称: Aes128EncryptAndDecrypTest()
*
* 功    能: 测试AES-128 加解密,注意我们加密时需要key,
*		      加密后的数据用于通信;同样解密的时候也
*		      需要用同一个key进行解密,这样,如果对方
*		      没有key 就无法解密出原始数据,进而保护了
*		      用户数据不被截取。
*
* 入口参数: 无    
* 出口参数: 无
****************************************************************/
static void Aes128EncryptAndDecrypTest(void)
{
	int i = 0;
	// 加密秘钥 16个字节也就是128 bit
	uint8 key[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

	// 需要加密的数据(保证16个字节,不够的自己填充)
	uint8 source_buf[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

	// 加密后数据存放区
	uint8 encrypted_buf[16];

	// 解密后数据存放区
	uint8 deccrypted_buf[16];

	// 开始加密,加密后的数据存放到encrypted_buf
	LL_Encrypt( key, source_buf, encrypted_buf );

	// 开始解密,将解密后的数据存到deccrypted_buf
	LL_EXT_Decrypt( key, encrypted_buf, deccrypted_buf );

	//打印原始数据
	tx_printf("source:");
	for(i = 0;i < 16;i++)
	{
		txprintf("0x%02x ",source_buf[i]);
	}
	tx_printf("");
	//打印加密后的数据
	tx_printf("encrypte:");
	for(i = 0;i < 16;i++)
	{
		txprintf("0x%02x ",encrypted_buf[i]);
	}
	tx_printf("");
	//打印解密后的数据
	tx_printf("deccrypte:");
	for(i = 0;i < 16;i++)
	{
		txprintf("0x%02x ",deccrypted_buf[i]);
	}
	tx_printf("");

}

上述测试方法放到simpleBLEPeripheral.c文件中,在初始化函数SimpleBLEPeripheral_Init里面最后的地方调用上述测试方法Aes128EncryptAndDecrypTest();就可以了。

 

通过上述测试方法,我们看到设备端加密用的是LL_Encrypt方法,解密用的是LL_EXT_Decrypt方法,这两个方法的声明在Ll.h头文件中,这个头文件被hci.h头文件引用,所以如果提示找不到这两个方法的时候,我们引用hci.h头文件即可。因为上面的注释比较详细,所以我们就不再赘述了。


(3)测试结果:



2.安卓手机端测试程序

(1)测试环境:

开发环境:Eclipse开发工具,Windows XP系统。

测试设备:Eclipse开发工具编译java工程可以在控制台输出结果。

(2)测试源码:

package com.zzfenglin.aes;

import java.util.Formatter;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AesEntryDetry {
	// 加密秘钥 ,16个字节也就是128 bit
	private static final byte[] AES_KEY = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
			12, 13, 14, 15, 16 };

	// 需要加密的数据(保证16个字节,不够的自己填充)
	private static final byte[] SOURCE_BUF = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
			11, 12, 13, 14, 15, 16 };

	// Java测试工程入口方法,在这个方法中调用加解密方法并打印结果
	public static void main(String[] args) throws Exception {
		// 需要加密的原始数据转化成字符串并打印到控制台
		String strSource = BytetohexString(SOURCE_BUF);
		System.out.println("source:\n" + strSource);

		// 调用加密方法,对数据进行加密,加密后的数据存放到encryBuf字节数组中
		byte[] encryBuf = encrypt(AES_KEY, SOURCE_BUF);
		// 将加密后的字节数组数据转成字符串并打印到控制台
		String strEncry = BytetohexString(encryBuf).toLowerCase();
		System.out.println("encrypte:\n" + strEncry);

		// 调用解密方法,对数据进行解密,解密后的数据存放到decryBuf字节数组中
		byte[] decryBuf = decrypt(AES_KEY, encryBuf);
		// 将解密后的字节数组数据转成字符串并打印到控制台
		String strDecry = BytetohexString(decryBuf);
		System.out.println("decrypte:\n" + strDecry);

	}

	// 加密方法
	private static byte[] encrypt(byte[] key, byte[] clear) throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		byte[] encrypted = cipher.doFinal(clear);
		return encrypted;
	}

	// 解密方法
	private static byte[] decrypt(byte[] key, byte[] encrypted)
			throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		byte[] decrypted = cipher.doFinal(encrypted);
		return decrypted;
	}

	// 字节数组按照一定格式转换拼装成字符串用于打印显示
	private static String BytetohexString(byte[] b) {
		int len = b.length;
		StringBuilder sb = new StringBuilder(b.length * (2 + 1));
		Formatter formatter = new Formatter(sb);

		for (int i = 0; i < len; i++) {
			if (i < len - 1)
				formatter.format("0x%02X:", b[i]);
			else
				formatter.format("0x%02X", b[i]);

		}
		formatter.close();

		return sb.toString();
	}
}

Java测试实例工程的CSDN下载链接如下:

http://download.csdn.net/detail/zzfenglin/9555823


(3)测试结果:

低功耗蓝牙BLE之AES-128加密算法_第1张图片


3.通过BLE设备端测试程序和安卓手机端测试程序结果的比较,我们发现原始有效数据和加密的Key都一样的情况下,二者加密的数据和解密后的数据也都一致,这就保证了设备端把加密数据发给安卓手机端之后,安卓手机端可以成功解密,并且安卓手机端发送加密数据给设备端之后,设备端也可以成功解密。上述的安卓手机端测试代码是在Java工程中,后期要在安卓工程中使用的话,只需要将相关的方法拷贝出去,或者将上述测试的Java文件改写成安卓工程中的一个工具类即可。




Java端参考资料


AES参考代码如下:

算法/模式/填充			16字节加密后数据长度			不满16字节加密后长度 
AES/CBC/NoPadding				16						不支持 
AES/CBC/PKCS5Padding				32						16 
AES/CBC/ISO10126Padding			32						16 
AES/CFB/NoPadding				16						原始数据长度 
AES/CFB/PKCS5Padding				32						16 
AES/CFB/ISO10126Padding			32						16 
AES/ECB/NoPadding				16						不支持 
AES/ECB/PKCS5Padding				32						16 
AES/ECB/ISO10126Padding			32						16 
AES/OFB/NoPadding				16						原始数据长度 
AES/OFB/PKCS5Padding				32						16 
AES/OFB/ISO10126Padding			32						16 
AES/PCBC/NoPadding				16						不支持 
AES/PCBC/PKCS5Padding			32						16 
AES/PCBC/ISO10126Padding			32						16

可以看到,在原始数据长度为16的整数倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],除了NoPadding填充之外的任何方式,加密数据长度都等于16*(n+1);NoPadding填充情况下,CBC、ECB和PCBC三种模式是不支持的,CFB、OFB两种模式下则加密数据长度等于原始数据长度。

你可能感兴趣的:(BLE)