1. 程式人生 > >對基於Lua和Nginx的iOS應用資料的加密框架

對基於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中的基本函式庫。

在這裡,為了完成加密任務,我選擇了基於Lua的C擴充套件庫來完成。開始還有一種想法就是直接在lua中通過lua的自定義資料結構像基本庫一樣去實現加密模組,但是考慮到lua升級的維護性和時間安排,最終放棄了這種選擇。目標確定之後,接下來就是開始編寫基於Lua的DES擴充套件庫,所有的輔助函式均可以用C語言實現,只需要稍微改變一下lua呼叫函式的形式即可。因為lua呼叫函式最終會在lua指令碼中呼叫,所以其函式所有引數的獲得都是從lua環境中獲得,lua提供了一個lua環境,其所有的引數都在稱作lua_State *L的引數裡,可以通過luaL_checkNumber(L, index), luaL_ChechString(L,index),分別讀取函式引數,然後通過註冊所有的lua呼叫函式,註冊擴充套件庫。這樣關於DES的擴充套件庫的程式碼工作就完成了。具體程式碼如下所示:

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的初步瞭解,歡迎指教。