NDK+OpenSSl,通过JNI技术开发so加密库

来源:互联网 发布:北京通州淘宝城 编辑:程序博客网 时间:2024/06/05 15:07

以下是个人在做加密算法库时一些经验总结,今天把它写下来分享给大家,希望对大家以后再做类似的开发工作时能有所帮助,少走些弯路。

主要从以下5个方面进行阐述:

1. Openssl安装,1.0.11.0.2使用时的区别

2. Linuxgcc的使用方法,及可能遇到的问题

3. JNI开发,开发流程

4. NDK使用方法,以及打包so库时如何编写Application.mk文件

5.IOS打包基于openssl库的静态库时需注意那些


一. Openssl安装,1.0.11.0.2使用时的区别

参考网上的教程:

① openssl官网下载opensslopenssl-1.0.1e.tar.gz

② 使用tar命令将其解压到/usr/local/src

③ Cd进入openssl-1.0.1e目录下,执行

 ./config  --prefix=/usr/local --openssldir=/usr/local/ssl

make && make install
./config shared --prefix=/usr/local --openssldir=/usr/local/ssl
make clean
make && make install

以上方法是我从网上copy下来的,若自己的机器上没有openssl,可以参考安装。

查看自己机器上是否已经安装openssl的方法,执行openssl  version,若有版本信息则已安装,否则未安装。

在使用Openssl库开发时,需要注意1.0.11.0.2中有些方法的参数是不同的。例如1.0.1bio.h中有个方法BIO *BIO_new_mem_buf(void *buf, int len),而其在1.0.2中则是BIO *BIO_new_mem_buf(const void *buf, int len)。所以在打包时要注意,开发环境和打包环境中的库是不是同一个版本,若不是需要在调用某些方法时稍作改动,一般使用指针类型强转下就可以解决。

二. Linuxgcc的使用方法,及可能遇到的问题

gcc主要是用来编译.c文件,编译.cpp文件可以使用g++。先介绍下一般会用到的几个命令参数:

-o指定输出文件名

-c只编译不连接

-g产生供gdb调试用的可执行文件

-l链接库,后跟库名,linux下的库名一般是lib+库名+.so形式

-L指定连接库路径

-I指定include文件路径

-fPIC在编译阶段使用,告诉编译器产生与位置无关代码,一般在编译动态共享库用到。

-shared设置共享,一般是在编译动态共享库时会用到。

用例:hello.c

编译成可执行文件:gcc  hello.c  –o  main,这要求hello.c中必须有int main()函数的实现。

编译生成中间文件:gcc  hello.c  -o hello.ohello.c中不需要有main函数的实现,生成的.o文件可以用于编译动态库或者是静态库。

gcc编译基于openssl开发的.c文件时,可能会遇到的问题

①  在编译的阶段提示找不到openssl/aes.h头文件

原因:opensslinclude文件未加入系统路径,导致gcc在编译时搜索不到

解决方法:先找到openssl文件夹路径,一般是在/usr/include/下,在编译时使用-I命令指定路径-I/usr/include/openssl即可。

②  在生成动态库时提示文件中使用的openssl库中方法undefined

原因:在生成动态库时,未指定依赖的第三方库

解决方法:-lssl即可,若是还需要依赖自己的其他库,还需要使用-L命令指定自己的库路径。

三. JNI开发,以及开发流程

JNI开发流程主要如下:

① 编写java文件,申明native方法,例如helloworld.java

public class helloworld{

public native void say();

static {

System.loadLibrary("hello");

}

public static void main(String[] args){

new helloworld().say();

}

}

② javac  helloworld.java生成helloworld.class文件

③ javah  helloworld生成helloworld.h文件

④ 编写hello.c文件,#includehelloworld.h”,实现文件中的方法

⑤ 生成编译文件gcc  -fPIC  -g  -c  hello.c  -o  libhello.o

⑥ 生成共享库gcc  -shared  libhello.o  -o libhello.so

⑦ java  hellworld

可能存在的问题:

(1) 在第⑤步可能会报找不到jni.h文件

原因: jni.h文件为加入到系统路径中,gcc在编译时找不到jni.h文件

解决方法:-I/opt/soft/java/include  –l/opt/soft/java/include/linux。不同系统路径不一样,这是我的系统路径。

(2) 在第⑦步会报找不到hello

原因:生成的共享库未加入到系统路径中

解决方法:export  LD_LIBRARY_PATH=第⑥步生成的共享库路径

(3) ④步可能会生成不成功

原因:在.java中有package语句,这会导致在本路径下生成.h不成功

解决方法:可以手动书写.java对应的.h文件,书写规则如下:

(a) 样版

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class hello */

#ifndef _Included_com_test_demo_hello

#define _Included_com_test_demo_hello

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class:     com_test_demo_hello

* Method:    say

* Signature: (Landroid/content/Context;)V

*/

JNIEXPORT void JNICALL Java_ com_test_demo_hello_say

(JNIEnv *, jclass, jobject);

#ifdef __cplusplus

}

#endif

#endif

(b) 其中红色表示的是该类的全路径名,浅蓝色表示的是类的方法,紫色表示的是jni环境指针,橙色表示的是该类对象(若是静态方法,则是jclass,若是非静态方法则是jobject),浅绿色是方法的参数。

(c) JNI中类型对照表:JNI类型——》JAVA类型

V

void

void

N/A

Z

jboolean

boolean

 8 unsigned

I

jint

int

 32

J

jlong

long

 64

D

jdouble

double

 64

F

jfloat

float

32

B

jbyte

byte

 8

C

jchar

char

 16 unsigned

S

jshort

short

 16

 

[I

jintArray

int[]

[F

jfloatArray

float[]

[B

jbyteArray

byte[]

[C

jcharArray

char[]

[S

jshortArray

short[]

[D

jdoubleArray

double[]

[J

jlongArray

long[]

[Z

jbooleanArray

Boolean[]

jchar -> jstring

jstring result = env->NewString(jchar *, jsize);  直接将jchar转换为jstring(Java String)可直接由JNI返回给Java使用。

jcharArray -> jchar*

jchar * jc = (*env)->GetCharArrayElements(env,jcharArray,0);

jbyteArray -> jbyte

jbyte * jby = (*env)->GetByteArrayElements(env,jbyteArray,0);

注意jchar并不能强转成c中的char,只有jbyte才与c中的char对应。

(d) JNI中一些常用的方法:

GetStringUTFCharsjstring转换成为UTF-8格式的char*
GetStringCharsjstring转换成为Unicode格式的char*
ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
ReleaseStringChars释放指向Unicode格式的char*的指针
NewStringUTF创建一个UTF-8格式的String对象
NewString创建一个Unicode格式的String对象
GetStringUTFLengt获取UTF-8格式的char*的长度
GetStringLength获取Unicode格式的char*的长度

jsize len = (*env)->GetArrayLength(env, arr);

释放对象指针:

ReleaseBooleanArrayElements
ReleaseByteArrayElements
ReleaseCharArrayElements
ReleaseShortArrayElements
ReleaseIntArrayElements
ReleaseLongArrayElements
ReleaseFloatArrayElements
ReleaseDoubleArrayElements

四. NDK使用方法,以及打包so库时如何编写Application.mk

① NDKandroid提供的用于编译c程序的编译器,按装很简单,从官网上下载下来,解压到自己的目录中,然后设置文件权限为777,最后编辑/etc/profile文件,在文件尾添加NDK=/ndk解压路径

export PATH=$NDK:$PATH,然后保存,执行source  /etc/profile,然后执行echo $NDK若输出不为空,则配置成功。

② cd到自己的jni项目路径下,执行$NDK/ndk-build即可。注意你的.c文件的父目录一定要是jni,即/…/jni/hello.c,生成的.o文件是在../obj/目录下,生成的库文件是在../libs/目录下。

NDK编译生成库文件的主要难点是编写Application.mk文件,下面给出一个链接,供大家学习:http://www.cnblogs.com/leaven/archive/2011/01/25/1944688.html

 

五. IOS打包基于openssl库的静态库时需注意那些

最好不要使用内存分配,即不要使用malloc,limux下使用malloc分配内存后会使用delete或者free释放内存,但是移植到ios下后会出现内存释放异常情况,不兼容,建议改成在栈空间上分配,或者使用Object C去实现。若本地没有openssl可以通过以下方式导入pod search openssl 或者 pod 引入 git@github.com:openssl/openssl.git

0 0
原创粉丝点击