赞
踩
蓝牙数据是可以通过空中抓包而被抓取到的,因此需要将通信数据进行加密,即使别人截获了加密后的数据,也无法利用该数据。
CC254x 支持对称加密 AES:
加密过程:
需要加密的数据 A 与秘钥 KEY 进行一定的算法,获得加密过的数据 B。
解密过程:
加密过的数据 B 与秘钥 KEY 进行一定的逆运算算法,获得加密前的数据 A。
因此,在 BLE 连接交互数据时,可以对明文数据进行加密,确保数据的机密性,从而抵御攻击者。机密性是指第三方“攻击者”由于没有加密链路的共享密钥,因此无法拦截、破译或读取消息的原始内容。
1、BLE AES-128 加密算法简介
低功耗蓝牙 BLE 中的所有加密和认证都基于同一个加密引擎,称为高级加密系统(AES)。
BLE 使用 128(16 字节) 位的密钥和 128 位的数据块。也就是说,所有密钥的长度均为 128 位,每次加密生成的密文长度为 16 个字节。
AES 加密块非常简单,它包含两个输入和一个输出。
两个输入分别为 128 位的密钥值和 128 位的纯文本数据块,输出则为 128 位的加密数据块。
密钥和纯文本在使用上有一些不同:
纯文本可以直接为加密块使用,但密钥必须经过处理后才能使用。
在 BLE 中 AES 加密引擎被用于下列四个基本功能:
1)加密净荷数据
2)计算消息完整性校验值
3)数据签名
4)生成私有地址
数据签名在安全管理器中定义,生成私有地址在通用访问规范中定义。
2、BLE AES-128 加解密方法源码
2.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”头文件即可。
2.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();
- }
- }
3、如何使用 MAC 生成 AES 秘钥
例子:该方式可以保证每个设备的 AES 秘钥不同,但如果他人知道了秘钥的获取方式,则该加密方式将不再安全。
CC254x 将温度值放在广播数据中发送给手机 app:
1)CC254x 使用 mac 形成 AES 秘钥;
2)CC254x 将温度值利用 AES 秘钥进行加密;
3)CC254x 将加密后的温度值动态广播出来;
4)手机 app 使用 mac 形成 AES 秘钥。
5)手机app获取CC2541的广播数据,并解析出温度数据字段。
6)将加密后的温度值利用AES秘钥进行解密。
-
- //******************************************************************************
- //name: GUA_AES.c
- //introduce: AES驱动
- //author:
- //******************************************************************************
- #include <ioCC2540.h>
- #include "LL.h"
- #include "GUA_AES.h"
-
- /*********************内部变量************************/
- static GUA_U8 sbGUA_AES_Key[16] = {0}; //AES的秘钥
-
- /*********************内部函数************************/
- static void GUA_Get_LocalMac(GUA_U8 *pGUA_LocalMac);
- static void GUA_AES_GetKey(GUA_U8 *pGUA_AES_LocalMac, GUA_U8 *pGUA_AES_Key);
-
- //******************************************************************************
- //name: GUA_Get_LocalMac
- //introduce: 获取本机mac
- //parameter: pGUA_LocalMac:mac需要保存到的位置,需要6个字节大小
- //return: none
- //******************************************************************************
- static void GUA_Get_LocalMac(GUA_U8 *pGUA_LocalMac)
- {
- pGUA_LocalMac[5] = *(GUA_U8 *)(0x780E); //直接指向指针内容
- pGUA_LocalMac[4] = *(GUA_U8 *)(0x780F);
- pGUA_LocalMac[3] = *(GUA_U8 *)(0x7810);
- pGUA_LocalMac[2] = XREG(0x7811); //define函数直接读出数据
- pGUA_LocalMac[1] = XREG(0x7812);
- pGUA_LocalMac[0] = XREG(0x7813);
- }
-
- //******************************************************************************
- //name: GUA_AES_GetKey
- //introduce: 通过mac形成自定义秘钥
- //parameter: pGUA_AES_LocalMac:mac地址
- // pGUA_AES_Key:秘钥存放位置,需要16字节大小
- //return: none
- //author: 甜甜的大香瓜
- //email: 897503845@qq.com
- //QQ group: 香瓜BLE之CC2541(127442605)
- //changetime: 2017.03.29
- //******************************************************************************
- static void GUA_AES_GetKey(GUA_U8 *pGUA_AES_LocalMac, GUA_U8 *pGUA_AES_Key)
- {
- pGUA_AES_Key[0] = 'G';
- pGUA_AES_Key[1] = 'U';
- pGUA_AES_Key[2] = 'A';
- pGUA_AES_Key[3] = '#';
-
- pGUA_AES_Key[4] = pGUA_AES_LocalMac[0];
- pGUA_AES_Key[5] = pGUA_AES_LocalMac[1];
- pGUA_AES_Key[6] = pGUA_AES_LocalMac[2];
- pGUA_AES_Key[7] = pGUA_AES_LocalMac[3];
- pGUA_AES_Key[8] = pGUA_AES_LocalMac[4];
- pGUA_AES_Key[9] = pGUA_AES_LocalMac[5];
-
- pGUA_AES_Key[10] = pGUA_AES_LocalMac[0] + pGUA_AES_LocalMac[1];
- pGUA_AES_Key[11] = pGUA_AES_LocalMac[2] + pGUA_AES_LocalMac[3];
- pGUA_AES_Key[12] = pGUA_AES_LocalMac[4] + pGUA_AES_LocalMac[5];
-
- pGUA_AES_Key[13] = pGUA_AES_LocalMac[0] - pGUA_AES_LocalMac[1];
- pGUA_AES_Key[14] = pGUA_AES_LocalMac[2] - pGUA_AES_LocalMac[3];
- pGUA_AES_Key[15] = pGUA_AES_LocalMac[4] - pGUA_AES_LocalMac[5];
- }
-
- //******************************************************************************
- //name: GUA_AES_Encrypted
- //introduce: 加密16字节的数据
- //parameter: pGUA_AES_Data:要加密的数据缓存区,必须16字节
- // pGUA_AES_EncryptedData:加密后的数据缓存区,必须16字节
- //return: none
- //******************************************************************************
- void GUA_AES_Encrypted(GUA_U8 *pGUA_AES_Data, GUA_U8 *pGUA_AES_EncryptedData)
- {
- LL_Encrypt(sbGUA_AES_Key, pGUA_AES_Data, pGUA_AES_EncryptedData);
- }
-
- //******************************************************************************
- //name: GUA_AES_Deccrypted
- //introduce: 解密16字节以内的数据
- //parameter: pGUA_AES_EncryptedData:加密后的数据缓存区,必须16字节
- // pGUA_AES_DeccryptedData:解密后的数据缓存区,必须16字节
- //return: none
- //author: 甜甜的大香瓜
- //email: 897503845@qq.com
- //QQ group: 香瓜BLE之CC2541(127442605)
- //changetime: 2017.03.29
- //******************************************************************************
- void GUA_AES_Deccrypted(GUA_U8 *pGUA_AES_EncryptedData, GUA_U8 *pGUA_AES_DeccryptedData)
- {
- LL_EXT_Decrypt(sbGUA_AES_Key, pGUA_AES_EncryptedData, pGUA_AES_DeccryptedData);
- }
-
- //******************************************************************************
- //name: GUA_AES_Init
- //introduce: AES初始化
- //parameter: none
- //return: none
- //******************************************************************************
- void GUA_AES_Init(void)
- {
- GUA_U8 nbGUA_LocalMac[6] = {0};
-
- //获取mac
- GUA_Get_LocalMac(nbGUA_LocalMac);
-
- //通过mac获取秘钥
- GUA_AES_GetKey(nbGUA_LocalMac, sbGUA_AES_Key);
- }
refer:
https://blog.csdn.net/zzfenglin/article/details/51729300
https://blog.csdn.net/feilusia/article/details/68070791
https://blog.csdn.net/feilusia/article/details/50085225
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。