NDK+OpenSSl,通过JNI技术开发so加密库
2017-02-21 16:33
204 查看
以下是个人在做加密算法库时一些经验总结,今天把它写下来分享给大家,希望对大家以后再做类似的开发工作时能有所帮助,少走些弯路。
主要从以下5个方面进行阐述:
1. Openssl安装,1.0.1与1.0.2使用时的区别
2. Linux下gcc的使用方法,及可能遇到的问题
3. JNI开发,开发流程
4. NDK使用方法,以及打包so库时如何编写Application.mk文件
5.IOS打包基于openssl库的静态库时需注意那些
一. Openssl安装,1.0.1与1.0.2使用时的区别
参考网上的教程:
① 到openssl官网下载openssl包openssl-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.1和1.0.2中有些方法的参数是不同的。例如1.0.1的bio.h中有个方法BIO
*BIO_new_mem_buf(void *buf, int len),而其在1.0.2中则是BIO *BIO_new_mem_buf(const void *buf, int len)。所以在打包时要注意,开发环境和打包环境中的库是不是同一个版本,若不是需要在调用某些方法时稍作改动,一般使用指针类型强转下就可以解决。
二. Linux下gcc的使用方法,及可能遇到的问题
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.o,hello.c中不需要有main函数的实现,生成的.o文件可以用于编译动态库或者是静态库。
gcc编译基于openssl开发的.c文件时,可能会遇到的问题
① 在编译的阶段提示找不到openssl/aes.h头文件
原因:openssl的include文件未加入系统路径,导致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文件,#include“helloworld.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类型
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中一些常用的方法:
GetStringUTFChars将jstring转换成为UTF-8格式的char*
GetStringChars将jstring转换成为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
① NDK是android提供的用于编译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
主要从以下5个方面进行阐述:
1. Openssl安装,1.0.1与1.0.2使用时的区别
2. Linux下gcc的使用方法,及可能遇到的问题
3. JNI开发,开发流程
4. NDK使用方法,以及打包so库时如何编写Application.mk文件
5.IOS打包基于openssl库的静态库时需注意那些
一. Openssl安装,1.0.1与1.0.2使用时的区别
参考网上的教程:
① 到openssl官网下载openssl包openssl-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.1和1.0.2中有些方法的参数是不同的。例如1.0.1的bio.h中有个方法BIO
*BIO_new_mem_buf(void *buf, int len),而其在1.0.2中则是BIO *BIO_new_mem_buf(const void *buf, int len)。所以在打包时要注意,开发环境和打包环境中的库是不是同一个版本,若不是需要在调用某些方法时稍作改动,一般使用指针类型强转下就可以解决。
二. Linux下gcc的使用方法,及可能遇到的问题
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.o,hello.c中不需要有main函数的实现,生成的.o文件可以用于编译动态库或者是静态库。
gcc编译基于openssl开发的.c文件时,可能会遇到的问题
① 在编译的阶段提示找不到openssl/aes.h头文件
原因:openssl的include文件未加入系统路径,导致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文件,#include“helloworld.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[] |
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中一些常用的方法:
GetStringUTFChars将jstring转换成为UTF-8格式的char*
GetStringChars将jstring转换成为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
① NDK是android提供的用于编译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
相关文章推荐
- Ubuntu Feisty 下开发 JNI 应用步骤 (Java调用C/C++的技术)
- Web开发中的缓存技术之三:通过ETag实现缓存处理(ASP.NET MVC版)
- 【转】.Net中通过反射技术的应用----插件程序的开发入门
- Android开发中使用NDK在Eclipse中实现JNI技术
- android 开发中通过JNI无法向SD卡写入文件的解决办法
- 安卓应用开发通过java调用c++ jni的图文使用方法
- Java开发中什么情况下要用到JNI技术??
- 【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)!
- JNI(Java Native Interface)技术在嵌入式软件开发中的应用
- NDK开发(1)——JAVA通过JNI调用C代码详细步骤
- WF工作流技术内幕 —— 通过Web服务调用Workflow工作流(开发持久化工作流)
- .Net中通过反射技术的应用----插件程序的开发入门
- .Net中通过反射技术的应用----插件程序的开发入门
- Android-NDK开发示例--通过JNI获取MD5码
- 【学习Android NDK开发】native code通过JNI调用Java方法
- 通过JNI 技术实现 键盘监听
- .Net中通过反射技术的应用----插件程序的开发入门
- 【iOS-cocos2d-X 游戏开发之十三】cocos2dx通过Jni调用Android的Java层代码(下)
- 【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)!
- 【转·开发技术】C#实现 [忘记密码] 通过【邮箱取回密码】功能