低功耗藍芽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)測試結果: