本方案中采用的PHP扩展方式为:
Ø下载PHP对应版本的源码,在其中加入、生成扩展(如smsupport.so);
Ø然后针对安装同一版本的PHP(注意,不需要是源码安装的,可以通过yum install,apt-get install安装的),将smsupport.so放置到extension_dir中;在php.ini最后一行加入extension = smsupport.so;
Ø重启apached服务
Ø此后,在php文件中直接调用smsupport.so提供的方法即可。
注意点
Ø在进行扩展及测试的过程中我们需要设置PHP的SAFE_MODE为OFF,否则可能无法完成扩展或测试。SAFE_MODE默认为OFF。
Ø编译php扩展的环境和运行环境需要一致!
Ø建议先使用PHP执行脚本,此时提供错误信息较为丰富。
PHP扩展开发
1.从http://php.net/releases/ php中下载对应目标的PHP版本;
2.由于php依赖libxml2-dev,为此,首先通过apt-get install libxml2-dev或者yum install libxml2-dev(注libxml2-dev在不同Linux下名称不同,baidu下)
3.解压缩1中下载的php包,进入目录,运行./configure完成配置,此时会生成编写extension所需的一些文件
4.进入到php源码中的ext目录,执行./ext_skel --extname=smsupport,其会自动生成一系列目录及目录内的文件
5.在ext/smsupport目录中
a)在.c文件中找到const zend_function_entry cqwei_functions[] = {,在其后添加自己定义的函数
注意:自动生成的为c文件,由于本扩展中要用到cpp,强行将其改为cpp,在编译时在后端加上-lstdc++,使得编译成功
PHP_FE(testadd,NULL)/*For testing, remove later. */
PHP_FE(dcsSM2Verify,arg_dcsSM2Verify)
PHP_FE(dcsSM4Decrypt,arg_dcsSM4Decrypt)
注意点:
ü将PHP_FE的名称改成自己的函数,可以同时包含多个;
ü如果PHP_FE中包含的函数需要参数,则应加入参数信息,如arg_Verify,其中arg_Verify包含4个参数,arg_Decrypt包含2个参数。
nZEND_BEGIN_ARG_INFO_EX(arg_Verify,
0, 0, 1)
ZEND_ARG_INFO(0,user)
ZEND_ARG_INFO(0,pubKey)
ZEND_ARG_INFO(0,ticket)
ZEND_ARG_INFO(0,sig)
ZEND_END_ARG_INFO()
nZEND_BEGIN_ARG_INFO_EX(arg_Decrypt,0, 0, 1)
ZEND_ARG_INFO(0,key)
ZEND_ARG_INFO(0,cipher)
ZEND_END_ARG_INFO()
b)PHP_FUNCTION(testadd)在定义自定义函数testadd的函数体,如下所示为最简单的内容
PHP_FUNCTION(testadd)
{
zend_printf("testadd00");
}
c)PHP_FUNCTION(Verify),稍微复杂,因为有参数和返回值,示例如下
PHP_FUNCTION(Verify)
{
char * user;
char * pubKey;
char* ticket;
char * sig;
intuserLen,pubKeyLen,ticketLen,sigLen;
charpubKeyArray[64];
if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"ssss",&user,&userLen,&pubKey,&pubKeyLen,&ticket,&ticketLen,&sig,&sigLen)== FAILURE)
{//解析参数,参数为4个char *,s表示char
*,其他格式见下文中的表格
RETURN_FALSE;
return;
}
if(pubKeyLen != 128 || sigLen !=64)
{
zend_printf("the sm2 parameter isincorrect\n");
RETURN_FALSE;
return;
}
CECCPublicKey *verify = new CECCPublicKey();
loadHexStr(pubKey,(unsigned char *)pubKeyArray,64);
if(verify->SetPublicKey((constunsigned char*)pubKeyArray, 64) == 0)
{
zend_printf("the public key isincorrect\n");
delete verify;
RETURN_FALSE;//表示返回false
return;
}
if(verify->VerifyMessage((constunsigned char *)ticket, ticketLen, (const unsigned char *)sig, sigLen, (constchar *)user, userLen) != 1)
{
zend_printf("the signature is incorrect\n");
delete verify;
RETURN_FALSE;
return;
}
delete verify;
RETURN_TRUE;//表示返回true
return;
}
d)char *plain = emalloc(cipherLen);
efree (plain);//动态分配内存和释放
e)返回string变量RETVAL_STRINGL(plain, cipherLen, 1); plain为char *,cipherLen为长度
f)在.h文件中加入如下内容
PHP_FUNCTION(testadd);
PHP_FE(Verify);
PHP_FE(Decrypt);
g)在php源码/ext/smsupport下编译:cc -fpic -DCOMPILE_DL_SMSUPPORT=1 -I /usr/local/include -I../ -I../../main -I ../.. -I ../../TSRM -I ../../Zend -c smsupport.cppEllipticCurve.cpp sm3hash.cpp Mpi.cpp SMS4.cpp -lstdc++ -fpermissive-Wwrite-strings
其中除smsupport.cpp外为正常的cpp文件
h)在php源码/ext/smsupport下链接:cc -shared -L /usr/local/lib -rdynamic -o smsupport.so *.o -lstdc++
部署示例
1.在CentOS上搭建php+apache
a)Yum install php
b)Yum install httpd
c)Service httpd start
2.配置php
a)将上述生成的so文件(如smsupport.so)放置到extension_dir目录中,extension_dir路径可通过echo phpinfo()获取
b)Find / -name “php.ini”,加入extension
cqwei.so;(在较为靠后的位置加入)
c)Service httpd start
d)在php中直接调用方法即可
细节介绍
Øzend_parse_parameters
n如if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"ssss",&user,&userLen,&pubKey,&pubKeyLen,&ticket,&ticketLen,&sig,&sigLen)== FAILURE)
nZEND_NUM_ARGS()告诉Zend引擎要取得的参数的信息
nTSRMLS_CC用来确保线程安全
n返回值将被检查是SUCCESS还是FAILURE。
u通常情况下,zend_parse_parameters()将返回SUCCESS;
u然而,如果调用脚本试图传入太多或太少的参数,或者传入的参数不能被转为适当的类型,Zend会自动输出一条错误信息并优雅地将控制权还给调用脚本
n本例指定s表明此函数期望只传入一个参数,而且该参数应该被转为string数据类型并装入通过地址传入的char*变量。
n注意,还有一个int变量通过地址被传入zend_parse_parameters()。这使Zend引擎提供字符串的字节长度,如此二进制安全的函数不再需要依赖strlen(name)确定字符串的长度。实际上使用strlen(name)甚至得不到正确的结果,因为name可能在字符串结束之前包含一个或多个NULL字符。
n再比如if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a,&b, &return_long) == FAILURE) {
u这次你的数据类型字符串读起来像:“我要一个long(l),一个double(d)”。
u下一个管道字符表示其余的参数是可选的。如果函数调用时没有传入可选参数,那么zend_parse_parameters()将不会改变传给它的对应变量。
lb是用于Boolean。
u数据类型字符串后面的是a、b和return_long,它们按地址传递,这样zend_parse_parameters()可以将值装入它们。
u警告:在32位平台中经常不加区分地使用int和long,但是,当你的代码在64位硬件上编译时,在本该使用一个的地方使用另一个是很危险的。所以记住要把long用于整型,把int用于字符串的长度。
Ø表1显示不同的类型和对应的字母代码,以及可用于zend_parse_parameters()的C类型:
类型代码变量类型
Booleanbzend_bool
Longllong
Doubleddouble
Stringschar*, int
Resourcerzval*
Arrayazval*
Objectozval*
zvalzzval*
n你可能立刻注意到表1中的最后四个类型都是zval*。待会儿你将看到,PHP中实际使用zval数据类型存储所有的用户空间变量。三种“复杂”数据类型,资源、数组和对象,当它们的数据类型代码被用于zend_parse_parameters()时,Zend引擎会进行类型检查,但是因为在C中没有与它们对应的数据类型,所以不会执行类型转换。
Ø返回值
n返回值的设置方法,网上大多数是错误的,可以借鉴ext目录下其他的扩展。
mcrypt安装
Ø首先安装mcrypt:http://blog.csdn.net/zy112289/article/details/52840062
l先安装Libmcrypt
#tar -zxvf libmcrypt-2.5.8.tar.gz
#cd libmcrypt-2.5.8
#./configure
#make
#make install说明:libmcript默认安装在/usr/local
l安装mhash
#tar -zxvf mhash-0.9.9.9.tar.gz
#cd mhash-0.9.9.9
#./configure
#make
#make install
l安装mcrypt
#tar -zxvf mcrypt-2.6.8.tar.gz
#cd mcrypt-2.6.8
#LD_LIBRARY_PATH=/usr/local/lib ./configure//注意,在一行输入
#make
#make install
Ø安装phpize: yum install php-devel
Ø编译、安装mcrypt
ncd php-5.3.3/ext/mcrypt/
nphpize
n./configure --with-php-config=/usr/bin/php-config
nmake && make install
n提示:Installing shared extensions:/usr/lib64/php/modules/,说明安装成功
n在php.ini中加入extension = mcrypt.so以加入扩展。
�$