這兩年的工作越來越常碰到有關 “Security” 的需求。簡單的說,就是資料要加密、通訊要加密。昨天有客戶來詢問可能的合作方案,也是有關於 Security 方面的東西。我一直覺得終端使用者其實不太在乎安全的問題,也許是用戶已經將這個視為理所當然,出包的時候才驚覺”居然沒加密”。
記得之前台北市的 pay.taipei,被傳出傳回的帳號密碼是沒加密的 (新聞出處),連美國的無人機視訊畫面也沒加密(新聞出處)。原來大家是真的不在乎…..直到被抓包前。
Tiny AES 加密 Library
其實 AES 加密的工具已經很普遍了,openssl, Linux kernel, GnuPG 等軟體都提供了各式各樣的的加密演算法,Linux kernel 也可以利用硬體幫助加解密速度。但在一些小場合,移植這些東西就是個大工程了,而且通常只需要其中一小部份的功能而已。
最近工作上就有需要做加密的工作,對速度也不要求。所以就上網找到了這個 Tiny AES Library 它提供了包含了 128, 192, 256 bits 的 AES ECB, CBC, 與 CTR mode 的加密功能。這樣已足以符加絕大部份的加密需求了,又很容易移植,所以就把它收錄下來,另外寫個簡單的例子做展示。
AES 模式 ECB, CBC, 與 CTR
稍微詳細的介紹,可以看 Wiki 上的資料。簡單的說 ECB 模式不安全、CBC 不適合用在會掉資料的狀況(ex.網路),而且資料一定要是16的倍數。CTR 可以平行加解密、中間的資料掉了後面也解的出來、資料不需是16倍數。
ECB 不安全當然就不用考慮。CBC 掉一個bit,後面的資料就會全錯,也不能平行加解密。聽起來好像是缺點,但這樣可以拖慢破解的速度,保証資料是完整的,也是有其需求。CTR 就通用一點,雖然其IV的產生方式就是不停的+1,看似好像有點弱,但在理論分析上,強度跟 CBC 是差不多的。所以,沒事就用 CTR Mode 就好了….
AES CTR 加解密範例
附件有精簡過後的 Tiny AES Library CTR 範例,下面解說一下程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
#include <stdio.h> #include <string.h> #include "aes.h" int main(void) { unsigned char key[] = { /* 加密的 KEY */ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, }; unsigned char iv[] = { /* Initial vector */ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, }; unsigned char buf[1024*128]; FILE *fp, *fp2; int i, ret, c; unsigned char empty[16]; #ifndef AES256 /* 此範例只允許 AES256 */ #error "Need to use AES256" #endif { // Do encryption struct AES_ctx ctx; /* 一次將檔案全讀進來,方便展示 */ fp=fopen("aes.c", "r"); ret = fread(buf, 1, sizeof(buf), fp); fclose(fp); fprintf(stderr, "Raw File len=%d, will gen aes.c.enc\n", ret); AES_init_ctx_iv(&ctx, key, iv); /*初始化加密 context, 指定key 和iv */ //CBC 的openssl equivalent 命令,和 tiney AES 的 function //openssl aes-256-cbc -e -nosalt -v -nopad -K 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef -iv 1234567890abcdef1234567890abcdef -in aes.c -out aes.c.openssl_enc //AES_CBC_encrypt_buffer(&ctx, buf, ret); //CTR 的 openssl equivalent 命令 //openssl aes-256-ctr -e -nosalt -v -nopad -K 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef -iv 1234567890abcdef1234567890abcdef -in aes.c -out aes.c.openssl_enc //加密後,結果會直接覆蓋原內容 AES_CTR_xcrypt_buffer(&ctx, buf, ret); fp2=fopen("aes.c.enc", "wb"); //將加密後的資料,寫入另一個檔案 for(i=0;i<ret;i++) { c= buf[i]; fputc(c, fp2); } fclose(fp2); } { //Do decryption struct AES_ctx ctx; //將加密後的檔案全部讀進 buffer fp=fopen("aes.c.enc", "r"); ret = fread(buf, 1, sizeof(buf), fp); fclose(fp); fprintf(stderr, "Encrypted len=%d, will gen aes.c.dec\n", ret); //初始化 context, 指定 key 和 iv AES_init_ctx_iv(&ctx, key, iv); //AES_CBC_decrypt_buffer(&ctx, buf, ret); //AES_CTR_xcrypt_buffer(&ctx, buf, ret); //此處為了模擬掉資料,我們以loop一次解 16byte. //即使是掉包,也要隨便丟個東西進去解。掉的那個 block 資料會錯 //但下個block就會正確了. for(i=0;i<ret/16;i++) { if(i == -1) { fprintf(stderr, "Insert a packet loss simulation in this test.\n"); //simulate packet loss. kick the counter AES_CTR_xcrypt_buffer(&ctx, empty, 16); continue; } //一次解密一個 block (16bytes) AES_CTR_xcrypt_buffer(&ctx, buf+i*16, 16); } fp2=fopen("aes.c.dec", "wb"); for(i=0;i<ret;i++) { c= buf[i]; fputc(c, fp2); } fclose(fp2); }// return 0; } |
感謝教學文,明天來試試看
現在有點晚了,如果有疑問我會再提出,謝謝
有問題歡迎一起討論