预答辩归来,主管给了我一个新任务,这是实习以来的第三个任务了。任务仍然很简单,就是对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的初步了解,欢迎指教。