一、应用场景
如果想用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 |
四、解决返回字符串乱码问题