對基於Lua和Nginx的iOS應用資料的加密框架
預答辯歸來,主管給了我一個新任務,這是實習以來的第三個任務了。任務仍然很簡單,就是對ios應用中的一些重要資料進行DES加密。
iOS的介面通過Webview形式展示,在web頁中通過JS呼叫lua指令碼對重要資料進行讀取和存放,所以加密模組的函式必須能夠在lua指令碼中呼叫。咋一看,將函式增加到“lua呼叫庫”裡是一個直接的方法。
第一種嘗試:增加lua的C API
通過對lua(programming with lua中文版,新浪資料)的基本學習瞭解到,lua作為一種簡單、高效執行、跨平臺的嵌入式指令碼語言,其主要由包括string、io等基本函式實現的標準庫構成,全部程式碼均有C語言實現。lua語言的另外一個特點是其可擴充套件性,通過函式棧的形式可以和所有的主流語言進行互動,可以在lua指令碼中呼叫其他語言構建的擴充套件庫,也可以在其他語言中通過lua執行環境呼叫lua中的基本函式庫。
static int DES_Encrypt(lua_State *L); static int DES_Decrypt(lua_State *L); //加密檔案 static int DES_Encrypt(lua_State *L){ const char * plainStr = luaL_checkstring(L, 1); int len_plainStr = luaL_checknumber(L, 2); const char * keyStr = DESKEY; int len_cipherStr = (len_plainStr + 8 - len_plainStr % 8); char *cipherStr = (char *)malloc(sizeof(char) * len_cipherStr); ElemType plainBlock[8],cipherBlock[8],keyBlock[8]; ElemType bKey[64]; ElemType subKeys[16][48]; //設定金鑰 memcpy(keyBlock,keyStr,8); //將金鑰轉換為二進位制流 Char8ToBit64(keyBlock,bKey); //生成子金鑰 DES_MakeSubKeys(bKey,subKeys); int len_remain = len_plainStr; while(len_remain > 0) { if(len_remain >= 8) memcpy(plainBlock, plainStr+(len_plainStr-len_remain), 8); else{ //讀取剩下的位元組,並補齊 memcpy(plainBlock, plainStr+(len_plainStr-len_remain), len_remain); memset(plainBlock+len_remain, '\0', 8-len_remain); plainBlock[7] = 8 - len_remain; } //加密每個block DES_EncryptBlock(plainBlock,subKeys,cipherBlock); //將生成的密文存入到一個數據結構中去 memcpy(cipherStr+(len_plainStr-len_remain), cipherBlock, 8); len_remain = len_remain - 8; } lua_pushlstring(L, cipherStr, len_cipherStr); free(cipherStr); return 1; } //解密檔案 //int DES_Decrypt(char *cipherFile, char *keyStr,char *plainFile){ static int DES_Decrypt(lua_State *L){ const char * cipherStr = luaL_checkstring(L, 1); int len_cipherStr = luaL_checknumber(L, 2); const char * keyStr = DESKEY; //如果解密字串不是8的倍數,說明該字串未經過加密 int len_plainStr = len_cipherStr; char *plainStr = (char *) malloc (sizeof(char) * len_plainStr); ElemType plainBlock[8],cipherBlock[8],keyBlock[8]; ElemType bKey[64]; ElemType subKeys[16][48]; //設定金鑰 memcpy(keyBlock,keyStr,8); //將金鑰轉換為二進位制流 Char8ToBit64(keyBlock,bKey); //生成子金鑰 DES_MakeSubKeys(bKey,subKeys); int len_remain = len_cipherStr; while(len_remain > 0){ //密文的位元組數一定是8的整數倍 memcpy(cipherBlock, cipherStr+(len_cipherStr-len_remain), 8); DES_DecryptBlock(cipherBlock,subKeys,plainBlock); memcpy(plainStr+(len_cipherStr-len_remain), plainBlock, 8); len_remain = len_remain - 8; } //判斷末尾是否被填充 if(plainBlock[7] < 8){ len_plainStr = len_plainStr - plainBlock[7]; } lua_pushlstring(L, plainStr, len_plainStr); free(plainStr); return 1; }
//將定義的函式名整合到一個結構陣列中去,建立 lua 中使用的方法名與 C 的函式名的對應關係
static const struct luaL_reg luades [] = {
{"lsin", l_sin},
{"lDesEncode", DES_Encrypt},
{"lDesDecode", DES_Decrypt},
{NULL, NULL} /* 必須以NULL結尾 */
};
//庫開啟時的執行函式(相當於這個庫的 main 函式),執行完這個函式後, lua 中就可以載入這個 so 庫了
int luaopen_luades (lua_State *L)
{
luaL_openlib(L, "myLuaDes", luades, 0);
return 1;
}
接下來就是編譯,首先編譯了一個mac版本的動態庫。就是通過將基於lua的des函式編譯成動態庫(.so形式),然後在lua的直譯器中載入lua_des擴充套件庫,通過自定義的擴充套件庫別名,呼叫自定義的函式別名進行呼叫。加密解密後的內容正確,說明函式功能完成。
gcc lDes.c -fPIC -W -Waggregate-return -Wcast-align -Wmissing-prototypes -Wnested-externs -Wshadow -Wwrite-strings -pedantic -I /Users/whaty/Desktop/LIBLUA/LIBLUA -L/Users/whaty/Desktop/LIBLUA/LIBLUA -shared -o libLuaDes.so -llua
package.loadlib("/Users/whaty/Desktop/LuaDes/LuaDes/libLuaDes.so", "luaopen_luades")()
print(mylib1.lsin(10))
但是現在的目標是需要在ipad應用中載入這個庫,這個.so形式的擴充套件庫肯定不能用,需要編譯一個ipad版本的動態庫。在ios中,動態庫是基於.dylib字尾名形式,如何將一個c檔案編譯成ipad動態庫的形式,這是一個思路,但是當前ios應用已有的一個架構是將lua標準庫編譯到Nginx中的庫裡,通過熟悉Nginx中的模組化思路我採取下面一種思路。
另一種嘗試:增加Nginx中Lua模組的庫函式功能
Nginx作為一種高效能的開源web伺服器,採用了模組化框架,所有模組都是通過靜態編譯,當啟動nginx時,會自動載入所有模組。其中Nginx_Lua模組提供了配置指令和Nginx API,配置指令是在nginx中使用,Nginx API是用來在Lua指令碼中訪問Nginx中的變數和函式。現在我們的目標是如何在Nginx中lua模組中加入一個新的Nginx API函式。
下面我們先來關注一下Nginx中Lua模組的執行機制。Nginx中的每個Worker程序使用一個lua虛擬機器,工作程序中的所有協程共享虛擬機器。將Nginx中的lua API封裝好之後注入到lua的VM中就可以在lua程式碼中進行訪問了。我沒有直接新開闢一個nginx api模組,而是在原來nginx.req API中加入一個DES模組。加密函式仍然不變,只是改變了以下注冊函式,註冊函式如下:
void
ngx_http_lua_inject_req_args_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args);
lua_setfield(L, -2, "set_uri_args");
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args);
lua_setfield(L, -2, "get_uri_args");
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args);
lua_setfield(L, -2, "get_query_args"); /* deprecated */
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_post_args);
lua_setfield(L, -2, "get_post_args");
lua_pushcfunction(L, DES_Encrypt);
lua_setfield(L, -2, "lDesEncode");
lua_pushcfunction(L, DES_Decrypt);
lua_setfield(L, -2, "lDesDecode");
//ngx.req.get_uri_args
}
註冊完畢之後,就可以在lua指令碼中通過ngx.req.lDesEncode() 和 ngx.req.lDesDecode()兩個函式進行加密和解密了。
上面只是本人對lua和nginx的初步瞭解,歡迎指教。