java使用JNI调用C++程序流程(含windows、linux下)

本文介绍如何在Windows和CentOS环境下使用Java调用C++代码实现SM4加密。通过JNI技术,首先在Windows下编写并编译Java与C++混合代码,接着移植到CentOS并编译为.so文件,实现跨平台加密功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文实现在windows下编写C++代码并使用java进行调用
然后将该C++代码进行响应修改在centos环境下使用java进行调用
整体是基于JNI实现SM4加密。

windows下实现

1.编写java代码 CallSM4.java

package com.sm4;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class CallSM4 {
    static {
        //linux下的so
        //System.load("/data/Sm4SDK/libsm4.so");
        //windows下的dll,放在JDK包下的bin文件夹中
        System.loadLibrary("CallSM4");
    }

    //ECB模式加密
    public native String encryptData_ECB(String plainText);

    //ECB模式解密
    public native String decryptData_ECB(String cipherText);

    //CBC模式加密
    public native String encryptData_CBC(String plainText);

    //CBC模式解密
    public native String decryptData_CBC(String cipherText);

    public static void main(String[] args) throws IOException {
        List<String> plainTextList = Arrays.asList("20208888");

        CallSM4 sm4 = new CallSM4();
        plainTextList.forEach(plainText -> {
            System.out.println("----------");
            System.out.println("ECB模式");
            String cipherText = sm4.encryptData_ECB(plainText);
            System.out.println("密文: " + cipherText);
            System.out.println("");

            plainText = sm4.decryptData_ECB(cipherText);
            System.out.println("明文: " + plainText);
            System.out.println("");

            System.out.println("CBC模式");
            cipherText = sm4.encryptData_CBC(plainText);
            System.out.println("密文: " + cipherText);
            System.out.println("");

            plainText = sm4.decryptData_CBC(cipherText);
            System.out.println("明文: " + plainText);
        });

    }
}

  1. 找到CallSM4.java文件所在路径,在该路径下打开命令行,使用如下指令:
 javac CallSM4.java

运行结束后,当前文件夹下出现同名的CallSM4.class文件

  1. 然后需要要使用javah对.java进行编译得到dll文件
    步骤:在.java的目录下使用命令行,执行如下指令
javah -classpath F:\XXX\XXX\XXX\ com.ais.sdsec.util.sm4.CallSM4

这里要注意路径每个人都不同,因为我的CallSM4.class是有包名com.sm4,所以这段路径F:\XXX\XXX\XXX\,而不是
F:\XXX\XXX\XXX\com\sm4

  1. 运行完上面步骤后在class边上就会得到一个com_sm4_CallSM4.h文件

---------------java部分暂时告一段落---------------

  1. 新建一个C++项目,本文使用Visual Studio。
    在这里插入图片描述

  2. 将上面得到的com_sm4_CallSM4.h引入到项目中
    同时在github上找到SM4加密的C++代码并下载,将其中的SM4.cpp与SM4.h均引入到项目中

  3. 接下来就要写我们自己得SM4的详细实现接口

#pragma warning( disable : 4996)
#include "com_sm4_CallSM4.h"
#include "sm4.h"
#include<iostream>
#include"base64.h"

//jstring转string
std::string jstring2str(JNIEnv* env, jstring jstr)
{
	char*   rtn = NULL;
	jclass   clsstring = env->FindClass("java/lang/String");
	jstring   strencode = env->NewStringUTF("GBK");
	jmethodID   mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
	jbyteArray   barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
	jsize   alen = env->GetArrayLength(barr);
	jbyte*   ba = env->GetByteArrayElements(barr, JNI_FALSE);
	if (alen > 0)
	{
		rtn = (char*)malloc(alen + 1);
		memcpy(rtn, ba, alen);
		rtn[alen] = 0;
	}
	env->ReleaseByteArrayElements(barr, ba, 0);
	std::string stemp(rtn);
	free(rtn);
	return   stemp;
}

//string转jstring
jstring str2jstring(JNIEnv* env, const char* pat)
{
	//定义java String类 strClass
	jclass strClass = (env)->FindClass("Ljava/lang/String;");
	//获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
	jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
	//建立byte数组
	jbyteArray bytes = (env)->NewByteArray(strlen(pat));
	//将char* 转换为byte数组
	(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
	// 设置String, 保存语言类型,用于byte数组转换至String时的参数
	jstring encoding = (env)->NewStringUTF("GBK");
	//将byte数组转换为java String,并输出
	return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}

//ECB模式:加密
JNIEXPORT jstring JNICALL Java_com_sm4_CallSM4_encryptData_1ECB(JNIEnv *env, jobject obj, jstring plainText)
{
	sm4 s;
	s.setType(sm4::ECB);
	s.setKey("111");
	s.setIv("222");
	//jstring转string
	std::string resTest = jstring2str(env, plainText);
	//std::cout << "resTest::" + resTest << std::endl;
	//string 加密
	std::string cipherTextUTF = s.encrypt(resTest);
	//加密后的string进行base64 encode
	std::string encodedStr = base64_encode(reinterpret_cast<const unsigned char*>(cipherTextUTF.c_str()), cipherTextUTF.length());
	//encoded后的string转换成jstring返回
	jstring base64CryJTest = (env)->NewStringUTF(encodedStr.c_str());
	return base64CryJTest;

}

//ECB模式:解密
JNIEXPORT jstring JNICALL Java_com_sm4_CallSM4_decryptData_1ECB(JNIEnv *env, jobject obj, jstring cipherText)
{
	sm4 s;
	s.setType(sm4::ECB);
	s.setKey("111");
	s.setIv("222");
	std::string res = jstring2str(env, cipherText);
	//std::cout << "cipherText::"+res << std::endl;

	//jstring转string
	std::string cipherTextStr = jstring2str(env, cipherText);
	//std::cout << "cipherTextStr::" + cipherTextStr << std::endl;
	//string 进行 base64的decode
	std::string decodedStr = s.decrypt(base64_decode(cipherTextStr));
	//std::cout << "decodedStr::" + decodedStr << std::endl;
	return str2jstring(env, decodedStr.c_str());


}

//CBC模式:加密
JNIEXPORT jstring JNICALL Java_com_sm4_CallSM4_encryptData_1CBC(JNIEnv *env, jobject obj, jstring plainText)
{
	sm4 s;
	s.setType(sm4::CBC);
	s.setKey("111");
	s.setIv("222");
	//jstring转string
	std::string resTest = jstring2str(env, plainText);
	//std::cout << "resTest::" + resTest << std::endl;
	//string 加密
	std::string cipherTextUTF = s.encrypt(resTest);
	//加密后的string进行base64 encode
	std::string encodedStr = base64_encode(reinterpret_cast<const unsigned char*>(cipherTextUTF.c_str()), cipherTextUTF.length());
	//encoded后的string转换成jstring返回
	jstring base64CryJTest = (env)->NewStringUTF(encodedStr.c_str());
	return base64CryJTest;

	
}

//CBC模式:解密
JNIEXPORT jstring JNICALL Java_com_sm4_CallSM4_decryptData_1CBC(JNIEnv *env, jobject obj, jstring cipherText)
{
	sm4 s;
	s.setType(sm4::CBC);
	s.setKey("111");
	s.setIv("222");
	std::string res = jstring2str(env, cipherText);
	//std::cout << "cipherText::" + res << std::endl;

	//jstring转string
	std::string cipherTextStr = jstring2str(env, cipherText);
	//std::cout << "cipherTextStr::" + cipherTextStr << std::endl;
	//string 进行 base64的decode
	std::string decodedStr = s.decrypt(base64_decode(cipherTextStr));
	//std::cout << "decodedStr::" + decodedStr << std::endl;
	return str2jstring(env, decodedStr.c_str());
}



要注意的点:
方法要和.h头文件中生成的方法名完全相同,到时候自动调用的时候就是通过相同的方法名进行对应调用。

  1. 然后生成dll文件
    右键解决右键项目,点击重新生成,则自动生成dll文件
    在这里插入图片描述
    这里要注意的是,
    一、要给你的c++项目添加上对java中jni文件的引用,如下图:(请根据自己的JDK安装目录对这个路径进行相应修改)
    在这里插入图片描述

二、因为我的java 的jdk是64位的,所以我之后进行java对dll动态链接库进行调用时,dll动态链接库也要求是64位的。
那么在dll生成时就要注意生成64位。
图中的几个位置都要换成x64
在这里插入图片描述

  1. 将上面得到的dll文件放入JDK安装目录下的bin文件夹中,如我的地址是:D:\JDK\bin,这样运行java文件时才能自动的找到这个动态链接库

  2. 然后回到IDEA 中,运行main函数,就可以看到teminal中的输出
    在这里插入图片描述

linux下实现

  1. 将上面的C++代码放入CentOS 中,使用makefile将其编译成so文件,生成的so文件一定是lib开头,so后缀的。
  2. 然后将java中的代码进行修改,只需要修改读取动态链接库部分,改成如下形式,(so文件的路径根据自己的进行修改)
static {
				//windows下的写法,因为CallSM.dll文件已经被放在JDK下的bin文件夹中,会自动找到
				//System.loadLibrary("CallSM4");
				//在Linux环境下,使用绝对路径找到so文件,并进行加载
        System.load("/data/Sm4SDK/libsm4.so");
    }
  1. 在linux上想执行Java文件,同时又满足对so文件的调用,因为本文使用了com.sm4
    因此需要新建相应的文件夹(即com→sm4),将java文件放在sm4文件夹中,然后在com的上一层目录进行java指令进行运行
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值