1. 程式人生 > >低功耗藍芽BLE之AES-128加密演算法

低功耗藍芽BLE之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下載連結如下:


(3)測試結果: