工作中需要用到java调用DES加密解密算法进行通信加密、MAC计算等,原来直接有C语言版的DES算法库可以用,但是java用的不熟,java的DES算法库没用过,就想到把C语言的DES算法库编译成DLL,然后通过java的JNI调用。
网上java调用jni的例子挺多,本以为挺简单的,但是实践了一下才知道,好多地方容易出错,这里总结一下容易出错的地方。
java调用JNI,按步骤,首先需要建一个类,声明本地方法。然后用javac编译这个类,最后用javah生成c语言的头文件并实现对应的*.c文件,并编译为动态库。把这个动态库放
在指定位置,如/bin目录下。在java中静态加载这个DLL。
以一个简单的例子说明:
package test; public class TestDes { public native void SayHello(String name); public native void DesOperation(int[] pInOut,int[] pbKey,int cnt); public native int MathDesMac(int[] pbuf,int len,int[] pbKey,int type); static { System.load(Class.class.getResource("/").getPath()+"test_TestDes.dll"); //System.load("C:/Users/yang/A303workspc/testjni/bin/test/test_TestDes.dll"); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("java jni test:"); System.out.println(Class.class.getResource("/").getPath()); //System.out.println(System.getProperty("user.dir")); TestDes h=new TestDes(); h.SayHello("nice to meet you!\r\n"); } }
常见的错误如:java.lang.UnsatisfiedLinkError,
javah -jni -classpath . test.TestDes
错误:无法访问 test.TestDes
javah -jni -classpath . TestDes
错误:无法访问 TestDes
错误的类文件: .\TestDes.class
类文件包含错误的类: test.TestDes
原因是有些地方需要注意:
当调用javah命令生成c语言的头文件时,首先需要进入*java的源文件目录下,调用javac 把有本地方法的java类文件编译为*.class,然后用javah命令时,需要在正确的目录下调用,切换回src目录下调用javah -jni命令。注意,指定的类clss文件要带上包名。如 test.TestDes
如在yang@DESKTOP-LRJFD2R ~/a303workspc/testjni/src 目录下,调用javah -jni -classpath . test.TestDes
其中test是包的名字。
生成了test_TestDes.h的头文件。
文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class test_TestDes */ #ifndef _Included_test_TestDes #define _Included_test_TestDes #ifdef __cplusplus extern "C" { #endif /* * Class: test_TestDes * Method: SayHello * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_test_TestDes_SayHello (JNIEnv *, jobject, jstring); /* * Class: test_TestDes * Method: DesOperation * Signature: ([I[II)V */ JNIEXPORT void JNICALL Java_test_TestDes_DesOperation (JNIEnv *, jobject, jintArray, jintArray, jint); /* * Class: test_TestDes * Method: MathDesMac * Signature: ([II[II)I */ JNIEXPORT jint JNICALL Java_test_TestDes_MathDesMac (JNIEnv *, jobject, jintArray, jint, jintArray, jint); #ifdef __cplusplus } #endif #endif
接下来就需要用GCC把这些头文件中的函数给实现了。为了方便,直接把JNI.h和JNI_md.h文件和需要编译的C文件都放到src目录下。
同时把test_TestDes.h头文件中的include<jni.h>,尖括号换为“JNI.h”
GCC生成动态库,如下:gcc -std=c99 -Wl,--add-stdcall-alias -shared
makefile文件:
######################################## #makefile ######################################## BINARY= test_TestDes CC= gcc LD= ld CFLAGS= -std=c99 -Wl,--add-stdcall-alias #LDSCRIPT= -lws2_32 #LDFLAGS= -Llib #OBJS= helloworld.o OBJS= curcalc_calc.o curcalc_crc.o curcalc_des.o curcalc_md5.o curcalc_oth.o curcalc_sha1.o test_TestDes.o #CFLAGS=-std=c99 .PHONY: clean all:images images: $(BINARY).dll $(OBJS):%.o:%.c $(CC) -c $(CFLAGS) $< -o $@ %.dll: $(OBJS) $(CC) $(CFLAGS) -shared -o $(BINARY).dll $(OBJS) cp $(BINARY).dll ../bin clean: rm -f *.o
test_TestDes.c文件如下:
#include "test_TestDes.h" #include <stdio.h> #include <string.h> #include "includes.h" void JNICALL Java_test_TestDes_SayHello(JNIEnv * env, jobject obj, jstring str) { jboolean b ; char s[80]; memset(s, 0, sizeof(s)); strcpy(s ,(char*)(*env)->GetStringUTFChars(env,str, &b)); printf("Hello, %s", s); (*env)->ReleaseStringUTFChars(env,str , NULL); } void JNICALL Java_test_TestDes_DesOperation(JNIEnv *env, jobject obj, jintArray data, jintArray key, jint t) { jboolean b ; jsize n1,n2; n1 = (*env)->GetArrayLength(env,data); n2 = (*env)->GetArrayLength(env,key); printf("len is %d\r\n",n1); if((n1 < 8) || (n2 < 8)) { printf("lenth is not enough!"); return; } jint jt1[n1]; jint jt2[n2]; unsigned char buf1[n1]; unsigned char buf2[n2]; //memcpy(s,(jint*)(*env)->GetIntArrayElements(env,data,&b),n); (*env)->GetIntArrayRegion(env,data,0,n1,jt1); for(int i=0;i < n1;i++) { buf1[i] = jt1[i]; } printf("jt1 is %d %d %d %d\r\n",jt1[0],jt1[1],jt1[2],jt1[3]); printf("buf1 is %d %d %d %d\r\n",buf1[0],buf1[1],buf1[2],buf1[3]); //printf("len is %d\r\n",n2); //memcpy(s,(jint*)(*env)->GetIntArrayElements(env,data,&b),n); (*env)->GetIntArrayRegion(env,key,0,n2,jt2); for(int i=0;i < n2;i++) { buf2[i] = jt2[i]; } CurCalc_DES_DesOperation( buf1, 0, buf2, SINGLE_DES_DECRYPTION | ZERO_CBC_IV, t ) ; printf("buf1 is %d %d %d %d\r\n",buf1[0],buf1[1],buf1[2],buf1[3]); (*env)->SetIntArrayRegion(env,data,0,8,jt1); } jint JNICALL Java_test_TestDes_MathDesMac(JNIEnv *env, jobject obj, jintArray data, jint len,jintArray key, jint type) { return 0; }
执行结果: