概述
互聯(lián)網(wǎng)時(shí)代,安全成為了一個(gè)沉重的話題。文件傳輸、電子郵件等的安全性尤為重要。我們?yōu)榱吮WC安全性,必須對(duì)其內(nèi)容加密,
加密的作用就是防止有用或私有化信息在傳輸鏈路上被攔截和竊取。提高數(shù)據(jù)傳輸?shù)目煽啃浴?/p>
在嵌入式開發(fā)中,我們會(huì)涉及到數(shù)據(jù)的傳輸,文件的傳輸。很多人都沒有考慮其數(shù)據(jù)的安全性問(wèn)題,往往都是明文的方式傳輸,最多增加CRC進(jìn)行數(shù)據(jù)的完整性校驗(yàn)。這明顯沒有考慮數(shù)據(jù)的安全性問(wèn)題。
最近項(xiàng)目遇到安全性問(wèn)題,所以也開始折磨一下加密相關(guān)知識(shí),發(fā)現(xiàn)RT-THREAD有個(gè)比較好軟件包--tinycrypt(一個(gè)簡(jiǎn)小并且可配置的加解密軟件包,包含算法:aes,base64,md5,sha1,sha2)。其中:AES剛好符合我的項(xiàng)目需求。
AES加密算法
AES加密標(biāo)準(zhǔn)又稱為高級(jí)加密標(biāo)準(zhǔn)Rijndael加密法,是美國(guó)國(guó)家標(biāo)準(zhǔn)技術(shù)研究所NIST旨在取代DES的21世紀(jì)的加密標(biāo)準(zhǔn)。AES的基本要求是,采用對(duì)稱分組密碼體制,密鑰長(zhǎng)度可以為128、192或256位,分組長(zhǎng)度128位。AES算法是最為常見的額對(duì)稱加密算法之一。
AES加密流程說(shuō)明:
AES的加解密流程圖如下:
加解密流程圖部件說(shuō)明:
明文P:沒有經(jīng)過(guò)加密的數(shù)據(jù)或文件。
密鑰K:用來(lái)加密明文P的密鑰,在對(duì)稱加密算法中,加密與解密的密鑰是相同的。密鑰為接收方與發(fā)送方協(xié)商產(chǎn)生,但不可以直接在網(wǎng)絡(luò)上傳輸,否則會(huì)導(dǎo)致密鑰泄漏,通常是通過(guò)非對(duì)稱加密算法加密密鑰,然后再通過(guò)網(wǎng)絡(luò)傳輸給對(duì)方,或者直接面對(duì)面商量密鑰。密鑰是絕對(duì)不可以泄漏的,否則會(huì)被攻擊者還原密文,竊取機(jī)密數(shù)據(jù)。
AES加密函數(shù):設(shè)AES加密函數(shù)為E,則 C = E(K + P),其中P為明文,K為密鑰,C為密文。也就是說(shuō),把明文P和密鑰K作為加密函數(shù)的參數(shù)輸入,則加密函數(shù)E會(huì)輸出密文C。
密文C:通過(guò)密鑰對(duì)明文進(jìn)行加密處理后的數(shù)據(jù)或文件。
AES解密函數(shù):設(shè)AES解密函數(shù)為D,則 P = D(K + C),其中C為密文,K為密鑰,P為明文。也就是說(shuō),把密文C和密鑰K作為解密函數(shù)的參數(shù)輸入,則解密函數(shù)會(huì)輸出明文P。
AES加密方法:
AES為分組加密,分組加密也就是把明文分成一組一組的,每組的長(zhǎng)度相等,每次加密一組數(shù)據(jù),直到加密完整個(gè)明文。
AES | 密鑰長(zhǎng)度(32bit) | 分組長(zhǎng)度(32bit) | 加密輪數(shù) |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
在AES標(biāo)準(zhǔn)規(guī)范中,分組長(zhǎng)度只能是128位,也就是說(shuō),每個(gè)分組為16個(gè)字節(jié),每個(gè)字節(jié)8位,密鑰的長(zhǎng)度可以使用128位、192位或者258位。密鑰的長(zhǎng)度不同,推薦加密輪數(shù)也不同,比如AES-128也就是密鑰的長(zhǎng)度為128位,加密輪數(shù)為10輪,AES-192為12輪,AES-256為14輪。以AES-128為例,加密中一輪的4個(gè)操作:
字節(jié)代換:AES的字符代換其實(shí)就是一個(gè)簡(jiǎn)單的查表操作,AES定義了一個(gè)S盒和一個(gè)逆S盒。
行位移:就是一個(gè)簡(jiǎn)單的左循環(huán)移位操作。
列混合:是通過(guò)矩陣相乘來(lái)實(shí)現(xiàn)的,經(jīng)過(guò)移位后的狀態(tài)矩陣與固定的矩陣相乘,得到混淆后的狀態(tài)矩陣。
輪密鑰加:是將128位輪密鑰Ki同狀態(tài)矩陣中的數(shù)據(jù)進(jìn)行逐位異或操作。
加密:加密第1輪到第9輪的輪函數(shù)一樣,最后一輪迭代不執(zhí)行列混合,另外,在第一輪迭代之前,先將明文和原始密鑰進(jìn)行一次異或加密操作。
解密:解密過(guò)程仍為10輪,每一輪的操作是加密操作的逆操作。由于AES的4個(gè)輪操作都是可逆的,因此,解密操作的一輪就是順序執(zhí)行逆行移位、逆字節(jié)代換、輪密鑰加和逆列混合。同加密操作類似,最后一輪不執(zhí)行逆列混合,在第1輪解密之前,要執(zhí)行1次密鑰加操作。
tinycrypt的AES使用
tinycrypt這個(gè)軟件包精簡(jiǎn)使用起來(lái)很方便,其包含了源碼和測(cè)試用例(AES和MD5),tinycrypt軟件包的目錄結(jié)構(gòu)如下:
├──?include │???├──?tiny_aes.h??????????????????//?aes?頭文件 │???├──?tiny_base64.h???????????????//?base64?頭文件 │???├──?tinycrypt_config.h??????????//?tinycrypt?配置頭文件 │???├──?tinycrypt.h?????????????????//?tinycrypt?頭文件 │???├──?tiny_md5.h??????????????????//?md5?頭文件 │???├──?tiny_sha1.h?????????????????//?sha1?頭文件 │???└──?tiny_sha2.h?????????????????//?sha2?頭文件 ├──?LICENSE ├──?README.md ├──?samples │???├──?aes_sample.c????????????????//?aes?測(cè)試用例頭文件 │???└──?md5_sample.c????????????????//?md5?測(cè)試用例頭文件 └──?src ????├──?tiny_aes.c??????????????????//?aes?源文件 ????├──?tiny_base64.c???????????????//?base64?源文件 ????├──?tiny_md5.c??????????????????//?md5?頭文件 ????├──?tiny_sha1.c?????????????????//?sha1?頭文件 ????└──?tiny_sha2.c?????????????????//?sha2?頭文件
在Ubuntu環(huán)境下,搭建tinycrypt的環(huán)境,測(cè)試加解密文件,目錄結(jié)構(gòu)如下:
├──?main.c ├──?makefile └──?tinycrypt ????├──?include ????│???├──?tiny_aes.h ????│???├──?tiny_base64.h ????│???├──?tinycrypt_config.h ????│???├──?tinycrypt.h ????│???├──?tiny_md5.h ????│???├──?tiny_sha1.h ????│???└──?tiny_sha2.h ????├──?LICENSE ????├──?README.md ????├──?samples ????│???├──?aes_sample.c ????│???└──?md5_sample.c ????└──?src ????????├──?tiny_aes.c ????????├──?tiny_base64.c ????????├──?tiny_md5.c ????????├──?tiny_sha1.c ????????└──?tiny_sha2.c
在目錄中增加makefile文件和main.c,其中makefile用于構(gòu)建工程,main.c用于編寫我們的測(cè)試代碼,其中:
makefile文件內(nèi)容:
VERSION?=?1.0.0?????????????????????????????????????????????#?版本號(hào) SOURCE??=?main.c????????????????????????????????????????????#?源文件 SOURCE?+=?$(wildcard?./tinycrypt/src/*.c)???????????????????#?通過(guò)wildcard函數(shù)獲取tinycrypt源文件 OBJECT??=?$(patsubst?%.c,?%.o,?$(SOURCE))???????????????????#?通過(guò)patsubst函數(shù)替換源文件為目標(biāo)文件 INCLUDE?=?-I?./tinycrypt/include????????????????????????????#?包含頭文件路徑 TARGET??=?tinycrypt?????????????????????????????????????????#?目標(biāo)文件名字 CC??????=?gcc???????????????????????????????????????????????#?工具鏈 CFLAGS??=?-Wall?-g??????????????????????????????????????????#?編譯選項(xiàng) OUTPUT??=?output????????????????????????????????????????????#?輸出目錄 $(TARGET):?$(OBJECT)????????????????????????????????????????#?構(gòu)建執(zhí)行命令 ?@mkdir?-p?$(OUTPUT)?????????????????????????????????????#?創(chuàng)建輸出目錄 ?$(CC)?$^?$(CFLAGS)?-o?$(OUTPUT)/$(TARGET)_$(VERSION)????#?編譯 %.o:?%.c????????????????????????????????????????????????????#?將源文件編譯成目標(biāo)文件 ?$(CC)?$(INCLUDE)?$(CFLAGS)?-c?$main.c文件內(nèi)容,測(cè)試歷程說(shuō)明:將一個(gè)文件(index.js)通過(guò)AES加密,然后生成加密文件(encrypt_index.js),再將加密文件(encrypt_index.js)通過(guò)AES解密,生成解密文件(decrypt_index.js)。
#include?#include? #include? #include? #include? #include? #include? #include? #include?"tiny_aes.h" #define?ENCRYPT_TEST_FILE?????"./index.js" #define?ENCRYPT_TEST_ENCRYPT_FILE???"./encrypt_index.js" #define?ENCRYPT_TEST_DECRYPT_FILE???"./decrypt_index.js" #define?ENCRYPT_SINGLE_SIZE????1024????????????????????????????????????//?加密單包長(zhǎng)度 #define?ENCRYPT_AES_IV_LEN?????16??????????????????????????????????????//?AES向量長(zhǎng)度 #define?ENCRYPT_AES_KEY_LEN????256?????????????????????????????????????//?AES密鑰長(zhǎng)度 #define?ENCRYPT_AES_IV??????"0123456789ABCDEF"??????????????????????//?AES向量 #define?ENCRYPT_AES_KEY?????"0123456789ABCDEF0123456789ABCDEF"??????//?AES密鑰 //?AES參數(shù)結(jié)構(gòu)體定義 typedef?struct { ?uint8_t?aesIv[ENCRYPT_AES_IV_LEN]; ?uint8_t?aesKey[ENCRYPT_AES_KEY_LEN]; ?tiny_aes_context?aes_ctx; }?EncryptInfo; static?EncryptInfo?g_info?=?{0}; //?aes?參數(shù)初始化 void?aes_init(const?char?*iv,?const?char?*key) { ?memset(g_info.aesIv,?0,?ENCRYPT_AES_IV_LEN); ?memset(g_info.aesKey,?0,?ENCRYPT_AES_KEY_LEN); ?if?(iv?==?NULL)?{ ??memcpy(g_info.aesIv,?ENCRYPT_AES_IV,?strlen(ENCRYPT_AES_IV)); ?} ?else?{ ??memcpy(g_info.aesIv,?iv,?strlen(iv)); ?} ?if(key?==?NULL)?{ ??memcpy(g_info.aesKey,?ENCRYPT_AES_KEY,?strlen(ENCRYPT_AES_KEY)); ?} ?else?{ ??memcpy(g_info.aesKey,?key,?strlen(key)); ?} } //?aes加密函數(shù)的封裝 void?aes_encrypt(uint8_t?*dest_buff,?const?uint8_t?*src_buff,?uint32_t?len) { ?tiny_aes_crypt_cbc(&g_info.aes_ctx,?AES_ENCRYPT,?len,?g_info.aesIv,?(uint8_t?*)src_buff,?dest_buff); } //?aes解密函數(shù)的封裝 void?aes_decrypt(uint8_t?*dest_buff,?const?uint8_t?*src_buff,?uint32_t?len) { ?tiny_aes_crypt_cbc(&g_info.aes_ctx,?AES_DECRYPT,?len,?g_info.aesIv,?(uint8_t?*)src_buff,?dest_buff); } int?main(int?argc,?char?*argv[]) { ?int?raw_size?=?0; ?int?src_fd?=?-1; ?int?encrypt_fd?=?-1; ?int?decrypt_fd?=?-1; ?int?src_file_size?=?0; ?int?srv_file_offset?=?0; ?uint8_t?src_buff[ENCRYPT_SINGLE_SIZE]; ?uint8_t?encrypt_buff[ENCRYPT_SINGLE_SIZE]; ?uint8_t?decrypt_buff[ENCRYPT_SINGLE_SIZE]; ?//?加密文件,將明文文件(index.js)通過(guò)AES加密為加密文件(encrypt_index.js) ????{ ????????aes_init(NULL,?NULL);???????????????????????????????????????????????????????????????????//?aes?初始化 ????????tiny_aes_setkey_enc(&g_info.aes_ctx,?(uint8_t?*)g_info.aesKey,?ENCRYPT_AES_KEY_LEN);????//?設(shè)置密鑰 ????????src_fd?=?open(ENCRYPT_TEST_FILE,?O_RDONLY,?0777);???????????????????????????????????????//?打開明文文件 ????????if?(src_fd?==?-1)?{ ????????????printf("%d-ERR:?open?file(%s)?failed ",?__LINE__,?ENCRYPT_TEST_FILE); ????????????return?-1; ????????} ????????encrypt_fd?=?open(ENCRYPT_TEST_ENCRYPT_FILE,?O_RDWR?|?O_CREAT?|?O_TRUNC,?0777);?????????//?打開加密文件 ????????if?(encrypt_fd?==?-1)?{ ????????????printf("%d-ERR:?open?file(%s)?failed ",?__LINE__,?ENCRYPT_TEST_ENCRYPT_FILE); ????????????return?-1; ????????} ????????raw_size?=?lseek(src_fd,?0,?SEEK_END);??????????????????????????????????????????????????//?獲取明文文件的大小 ????????srv_file_offset?=?0; ????????lseek(src_fd,?0,?SEEK_SET); ????????printf("%d-INFO:?source?file?size:?%d ",?__LINE__,?raw_size); ????????while?(srv_file_offset? 驗(yàn)證時(shí)發(fā)現(xiàn)一個(gè)問(wèn)題,加密內(nèi)容的長(zhǎng)度需要16字節(jié)的整數(shù)倍,所以加密完的文件大小也為16字節(jié)的整數(shù)倍。值得注意的是,解密的最后一包要根據(jù)明文文件的大小算出來(lái)的,然后寫進(jìn)解密文件中,因?yàn)榧用芪募?6字節(jié)對(duì)齊的,所以要去除16字節(jié)對(duì)齊。
編譯及運(yùn)行:
rice@rice:~/project/encrypt$?make gcc?-I?./tinycrypt/include?-Wall?-g?-c?main.c?-o?main.o gcc?-I?./tinycrypt/include?-Wall?-g?-c?tinycrypt/src/tiny_sha1.c?-o?tinycrypt/src/tiny_sha1.o gcc?-I?./tinycrypt/include?-Wall?-g?-c?tinycrypt/src/tiny_aes.c?-o?tinycrypt/src/tiny_aes.o gcc?-I?./tinycrypt/include?-Wall?-g?-c?tinycrypt/src/tiny_md5.c?-o?tinycrypt/src/tiny_md5.o gcc?-I?./tinycrypt/include?-Wall?-g?-c?tinycrypt/src/tiny_sha2.c?-o?tinycrypt/src/tiny_sha2.o gcc?-I?./tinycrypt/include?-Wall?-g?-c?tinycrypt/src/tiny_base64.c?-o?tinycrypt/src/tiny_base64.o gcc?main.o?tinycrypt/src/tiny_sha1.o?tinycrypt/src/tiny_aes.o?tinycrypt/src/tiny_md5.o?tinycrypt/src/tiny_sha2.o?tinycrypt/src/tiny_base64.o?-Wall?-g?-o?output/tinycrypt_1.0.0 rice@rice:~/ohos/project/encrypt$?./output/tinycrypt_1.0.0? 136-INFO:?source?file?size:?445 171-INFO:?encrypt?file?size:?445 rice@rice:~/ohos/project/encrypt$?演示結(jié)果:
結(jié)論:
通過(guò)加密方式,提高數(shù)據(jù)的安全性,避免了數(shù)據(jù)的泄露
AES加密方式,加密內(nèi)容的長(zhǎng)度需要16字節(jié)的整數(shù)倍,加密之后長(zhǎng)度比實(shí)際明文可能要長(zhǎng)。所以明文的長(zhǎng)度需要告知解密方。
AES是對(duì)稱加密,所以加密方和解密方的密鑰需要一直。
編輯:黃飛
?
評(píng)論