JNI调用dll库或so库

清疚 2022-09-06 01:20 280阅读 0赞

一、应用场景

  1. 如果想用Java调用CC++程序,前提是给定了CC++的动态库dllWindows)或soLinux)文件和函数头文件说明,根据头文件编写JNI文件,最后根据JNI文件编写Java程序。

二、应用过程

1、Windows环境

1.1、首先得到dll库文件和头文件说明

20210820110750914.png

​​watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjEwMg_size_16_color_FFFFFF_t_70

1.2、根据头文件编写JNI文件

1.2.1、编写JNI头文件cn.bk.test.Native.h

20210820141652653.png

  1. //引入jni头
  2. #include <jni.h>
  3. //此处声明具体的函数
  4. extern "C" {
  5. /*
  6. * Class: cn_bk_test_Native
  7. * Method: comEncryptionProcEX
  8. * Signature: (I[BI[B[I)I
  9. */
  10. JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
  11. (JNIEnv*, jclass, jint, jint, jint, jbyteArray, jint, jbyteArray, jintArray);
  12. }

1.2.2、编写JNI类文件cn.bk.test.Native.c

20210820141843643.png

  1. //引入头
  2. #include "cn_bk_test_Native.h"
  3. //----------------------------------------------------------------------------
  4. // 功能 调用密码机上的对称密钥索引号,进行加解密
  5. // 输入 comID 输入 输入:算法标识 输出:无
  6. // keyIndex 输入 输入:指定对称密钥索引号 输出:无
  7. // openSeal 输入 输入:加/解密标志 输出:无
  8. // inData 输入 输入:明文/密文 输出:无
  9. // inLen 输入 输入:明文/密文长度 输出:无
  10. // outData 输入/输出 输入:密文/明文存储空间 输出:密文/明文
  11. // outLen 输入/输出 输入:密文/明文存储空间长度 输出:密文/明文长度
  12. // 返回 0: 0
  13. // 错误,返回错误代码
  14. //----------------------------------------------------------------------------
  15. JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
  16. (JNIEnv* env, jclass jobj, jint comID, jint keyIndex, jint openSeal, jbyteArray inData, jint inLen, jbyteArray outData, jintArray outLen) {
  17. unsigned char* c_inData = (*env)->GetByteArrayElements(env, inData, NULL);
  18. unsigned char* c_outData = (*env)->GetByteArrayElements(env, outData, NULL);
  19. int* c_outLen = (*env)->GetIntArrayElements(env, outLen, NULL);
  20. int ret = comSymmetryCryptionProc_EX(comID, keyIndex, openSeal, c_inData, inLen, c_outData, c_outLen);
  21. (*env)->ReleaseByteArrayElements(env, inData, c_inData, 0);
  22. (*env)->ReleaseByteArrayElements(env, outData, c_outData, 0);
  23. (*env)->ReleaseIntArrayElements(env, outLen, c_outLen, 0);
  24. return ret;
  25. }

注意:

  • JNI中的函数的命名规则:Java_(Java中的包名)cn_bk_test_(Java中的类名)Native_(Java中的方法名)comEncryptionProcEX
  • JNI函数中不存在方法重构,即方法名不能重复

1.3、根据编写的JNI文件生成Java直接调用的dll文件

1.3.1、下载安装mingw_64位, 并将其安装目录配置到系统环境变量

  1. mingw\_64位最新下载地址:[https://sourceforge.net/projects/mingw-w64/files/][https_sourceforge.net_projects_mingw-w64_files]

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjEwMg_size_16_color_FFFFFF_t_70 1
该软件的作用是在windows系统使用gcc和g++命令,对c文件和c++文件进行编译,生成.o文件, 进而生成.dll文件。64位的只能生成64位的dll文件。若要生成32位的dll文件,请下载32位的mingw.
mingw_32最新版下载地址:https://sourceforge.net/projects/mingw/files/Installer/
安装完后将{minw安装目录}\mingw64\bin放到path即可。

1.3.2、生成 .o文件

  1. 执行命令: gcc -c -I"%JAVA_HOME%\include" - I"%JAVA_HOME%\include\win32" cn_bk_test_Native.c

1.3.2、生成 .dll文件

  1. 执行命令: gcc -Wl,--add-stdcall-alias -shared -o [要生成的dll文件]cn_bk_test_Native.dll [上一步生成的.o文件]cn_bk_test_Native.o [依赖的C程序dll文件]test64.dll

注:如果报系统位数错误则检查C程序性的dll文件编译环境与你此刻编译的系统环境是否相同。

1.4、编写最终调用的Java程序

1.4.1、创建Java工程

1.4.2、创建类:cn.bk.test.Native

  1. public class Native {
  2. /**
  3. * 调用密码机上的对称密钥索引号,进行加解密.
  4. *
  5. * @param comID 输入 算法标识 0x00200000(sm1) 0x00400000(sm4)
  6. * @param keyIndex 输入 指定对称密钥索引号(需要使用密码机管理工具,在索引号的位置生成128bit对称密钥)
  7. * @param openSeal 输入 加/解密标志:0 加密 1 解密
  8. * @param inData 输入 明文/密文
  9. * @param inLen 输入 明文/密文长度
  10. * @param outData 输出 密文/明文存储空间
  11. * @param outLen 输出 密文/明文存储空间长度
  12. * @return 返回错误代码
  13. */
  14. public static native int comEncryptionProcEX(
  15. int comID,
  16. int keyIndex,
  17. int openSeal,
  18. byte[] inData,
  19. int inLen,
  20. byte[] outData,
  21. int[] outLen
  22. );
  23. }

1.4.3、直接调用

  1. /**
  2. * SM1对称加解密.
  3. *
  4. * @param keyIndex 容器号
  5. * @param openSeal 0 加密 1 解密
  6. * @param srcData Base64编码待加密/解密数据
  7. * @return Base64编码加密/解密数据
  8. */
  9. public static void sm1CryptionProc(int keyIndex, int openSeal, String srcData) {
  10. byte[] inData = Base64Decoder.decode(srcData);
  11. //给足够大的空间
  12. byte[] outData = new byte[20000];
  13. int[] outDataLen = {20000};
  14. int n = Native.comEncryptionProcEX(0x00200000, keyIndex, openSeal, inData, inData.length, outData, outDataLen);
  15. if (n != 0) {
  16. System.out.println("失败");
  17. } else {
  18. System.out.println("成功,结果:" + Base64Encoder.encode(makeByteArray(outData, outDataLen[0])));
  19. }
  20. }
  21. /**
  22. * 拷贝有效数据.
  23. *
  24. * @param src 原数据
  25. * @param srcLen 原数据长度
  26. * @return 有效数据
  27. */
  28. public static byte[] makeByteArray(byte[] src, int srcLen) {
  29. byte[] finalData = new byte[srcLen];
  30. System.arraycopy(src, 0, finalData, 0, srcLen);
  31. return finalData;
  32. }

2、Linux环境

2.1、首先得到so库文件和头文件说明

20210901151208265.png

2.2、根据头文件编写JNI文件(同上)

2.3、根据编写的JNI文件生成Java直接调用的so文件

2.3.1、生成 .o文件

  1. 执行命令: cc -fPIC -c -I . -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/linux" cn_bk_test_Native.c -pthread

2.3.2、生成 .so文件

  1. 执行命令: gcc -fPIC -shared -o [要生成的so文件]libcn_bk_test_Native.so [上一步生成的.o文件]cn_bk_test_Native.o [依赖的C程序so文件]test64.so -lpthread

2.4、编写最终调用的Java程序(同上)

三、C-JNI-JAVA类型对照表






























































































C类型  JNI类型 Java类型
unsigned char jboolean Boolean
unsigned char jbyteArray byte[]
char jbyte Byte
char jstring String
unsigend short jchar Char
short jshort Short
int jint Integer
unsigned int jint int
unsigned int jintArray int[]
long long jlong Long
float jfloat Float
double jdouble Double
void** &jintArray int[]
SGD_UINT8 jbyteArray byte[]
SGD_UINT32 jint int
SGD_UINT32* jintArray int[]
DWORD jint int

四、解决返回字符串乱码问题

详见:https://www.jianshu.com/p/4f38bd3bccc0

发表评论

表情:
评论列表 (有 0 条评论,280人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JNI调用dllso

    一、应用场景         如果想用Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,根据头文

    相关 Java装载sodll文件

    1、调用目的 标准的java类库可能不支持你的程序所需的特性。或许你已经有了一个用其他语言写成的库或程序,而你希望在java程序中使用它。你可能需要用底层语言实现一个小型