JNI调用dll库或so库

  • Post author:
  • Post category:其他


一、应用场景

如果想用Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,根据头文件编写JNI文件,最后根据JNI文件编写Java程序。

二、应用过程

1、Windows环境

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

​​

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

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

//引入jni头
#include <jni.h>

//此处声明具体的函数
extern "C" {

    /*
     * Class:     cn_bk_test_Native
     * Method:    comEncryptionProcEX
     * Signature: (I[BI[B[I)I
     */
    JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
    (JNIEnv*, jclass, jint, jint, jint, jbyteArray, jint, jbyteArray, jintArray);

}

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

//引入头
#include "cn_bk_test_Native.h"


//----------------------------------------------------------------------------
// 功能	调用密码机上的对称密钥索引号,进行加解密
// 输入	comID		输入		输入:算法标识						输出:无
//		keyIndex	输入		输入:指定对称密钥索引号		        输出:无
//		openSeal	输入		输入:加/解密标志 					输出:无
//		inData		输入	 	输入:明文/密文 					输出:无
//		inLen		输入		输入:明文/密文长度 				输出:无
//		outData		输入/输出	输入:密文/明文存储空间 				输出:密文/明文
//		outLen		输入/输出	输入:密文/明文存储空间长度 		    输出:密文/明文长度
// 返回	0:  0
//		错误,返回错误代码
//----------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_cn_bk_test_Native_comEncryptionProcEX
(JNIEnv* env, jclass jobj, jint comID, jint keyIndex, jint openSeal, jbyteArray inData, jint inLen, jbyteArray outData, jintArray outLen) {

	unsigned char* c_inData = (*env)->GetByteArrayElements(env, inData, NULL);
	unsigned char* c_outData = (*env)->GetByteArrayElements(env, outData, NULL);
	int* c_outLen = (*env)->GetIntArrayElements(env, outLen, NULL);

	int ret = comSymmetryCryptionProc_EX(comID, keyIndex, openSeal, c_inData, inLen, c_outData, c_outLen);

	(*env)->ReleaseByteArrayElements(env, inData, c_inData, 0);
	(*env)->ReleaseByteArrayElements(env, outData, c_outData, 0);
	(*env)->ReleaseIntArrayElements(env, outLen, c_outLen, 0);

	return ret;
}


注意:


  • JNI中的函数的命名规则:Java_(Java中的包名)cn_bk_test_(Java中的类名)Native_(Java中的方法名)comEncryptionProcEX

  • JNI函数中不存在方法重构,即方法名不能重复

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

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

mingw_64位最新下载地址:

https://sourceforge.net/projects/mingw-w64/files/



该软件的作用是在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文件

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

1.3.2、生成 .dll文件

执行命令: 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

public class Native {

    /**
     * 调用密码机上的对称密钥索引号,进行加解密.
     *
     * @param comID    输入   算法标识 0x00200000(sm1) 0x00400000(sm4)
     * @param keyIndex 输入   指定对称密钥索引号(需要使用密码机管理工具,在索引号的位置生成128bit对称密钥)
     * @param openSeal 输入   加/解密标志:0 加密 1 解密
     * @param inData   输入   明文/密文
     * @param inLen    输入   明文/密文长度
     * @param outData  输出   密文/明文存储空间
     * @param outLen   输出   密文/明文存储空间长度
     * @return 返回错误代码
     */
    public static native int comEncryptionProcEX(
            int comID,
            int keyIndex,
            int openSeal,
            byte[] inData,
            int inLen,
            byte[] outData,
            int[] outLen
    );

}

1.4.3、直接调用

    /**
     * SM1对称加解密.
     *
     * @param keyIndex 容器号
     * @param openSeal 0 加密 1 解密
     * @param srcData  Base64编码待加密/解密数据
     * @return Base64编码加密/解密数据
     */
    public static void sm1CryptionProc(int keyIndex, int openSeal, String srcData) {

        byte[] inData = Base64Decoder.decode(srcData);
	//给足够大的空间
        byte[] outData = new byte[20000];
        int[] outDataLen = {20000};

        int n = Native.comEncryptionProcEX(0x00200000, keyIndex, openSeal, inData, inData.length, outData, outDataLen);
        if (n != 0) {
            System.out.println("失败");
        } else {
            System.out.println("成功,结果:" + Base64Encoder.encode(makeByteArray(outData, outDataLen[0])));
        }
    }



    /**
     * 拷贝有效数据.
     *
     * @param src    原数据
     * @param srcLen 原数据长度
     * @return 有效数据
     */
    public static byte[] makeByteArray(byte[] src, int srcLen) {

        byte[] finalData = new byte[srcLen];
        System.arraycopy(src, 0, finalData, 0, srcLen);

        return finalData;
    }

2、Linux环境

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

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

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

2.3.1、生成 .o文件

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

2.3.2、生成 .so文件

执行命令: 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



版权声明:本文为weixin_43192102原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。