Jni:使用openssl库进行rsa加密解密(实现篇)

前面,已经对于一个vs 2019 的jni环境的搭建已经做了说明。
免费机票 Jni:使用openssl库进行rsa加密解密 (环境搭建篇)

安装Openssl

windows安装:
百度网盘下载
链接:https://pan.baidu.com/s/10WlgBJ3J25oIH4JXVgWLOA
提取码:ABCD
无脑安装版,略过。这里你要记住你安装好的位置。例如我的是 C:\Program Files\OpenSSL-Win64

Centos安装:
这里我是在网上摘抄的。安装这些就不写了,不是本篇重点

mkdir /opt/openssl -p
cd /opt/openssl
wget http://www.openssl.org/source/openssl-1.0.2j.tar.gz
tar -zxf openssl-1.0.2j.tar.gz
./config --prefix=/usr/local/openssl
make & make install

vs 2019 中引入openssl库

1、右键项目,选择Properties,之后把 openssl的安装目录下的include加到以下位置(偷懒直接贴图)
在这里插入图片描述
2、添加openssl的lib到lib directories中
在这里插入图片描述

3、添加libssl.lib和libcrypto.lib 到link中
在这里插入图片描述
至此,你的openssl环境基本搭建好了

RSA加密解密

公钥加密
注意:准备的公钥需要以 “-----BEGIN PUBLIC KEY-----\n"开头,以”\n-----END PUBLIC KEY-----\n"结尾,里面的’\n’表示换号符
注意:准备的私钥需要以 “-----BEGIN RSA PRIVATE KEY-----\n"开头,以”\n-----END RSA PRIVATE KEY-----\n"结尾,里面的’\n’表示换号符

Jni的转换操作类,主要是为了方便java跟c++的类型转换方便
MyJniConvertUtils.h

#pragma once
#include <iostream>
#include <jni.h>
#include <string.h>
class MyJniConvertUtils
{
public:
        static int toCharArray(JNIEnv* env, jbyteArray byteArray, char*& dest);

        static int toCharArray(JNIEnv* env, jstring param, char*& dest);

        static jbyteArray toJbyteArray(JNIEnv* env, const char* data, const int length);

        static std::string getProperty(JNIEnv* env, std::string key);

        static std::string toString(const char* chars, const int length);

        static jstring toJString(JNIEnv* env, const std::string input);

        static std::string toString(JNIEnv* env, jstring param);
};

MyJniConvertUtils.cpp

#include "MyJniConvertUtils.h"
#include <string.h>

jstring MyJniConvertUtils::toJString(JNIEnv* env, const std::string input) {
        const char* cs = input.c_str();
        jclass strClass = (env)->FindClass("Ljava/lang/String;");
        jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
        jbyteArray bytes = (env)->NewByteArray(strlen(cs));
        (env)->SetByteArrayRegion(bytes, 0, strlen(cs), (jbyte*)cs);
        jstring encoding = (env)->NewStringUTF("UTF8");
        return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}

int MyJniConvertUtils::toCharArray(JNIEnv* env, jbyteArray byteArray, char*& dest) {
        jbyte* bytes;
        bytes = env->GetByteArrayElements(byteArray, 0);
        int byteLength = env->GetArrayLength(byteArray);
        int length = byteLength + 1;
        dest = new char[length];
        memset(dest, 0, length);
        memcpy(dest, bytes, byteLength);
        env->ReleaseByteArrayElements(byteArray, bytes, 0);
        return length;
}

int MyJniConvertUtils::toCharArray(JNIEnv* env, jstring param, char*& dest) {
        jclass clsstring = env->FindClass("java/lang/String");
        jstring strencode = env->NewStringUTF("UTF8");
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr = (jbyteArray)env->CallObjectMethod(param, mid, strencode);
        jsize   alen = env->GetArrayLength(barr);
        int size = ((int)alen + 1);
        dest = new char[size];
        memset(dest, 0, size);
        jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
        if (alen > 0)
        {
                memcpy(dest, ba, alen);
        }
        env->ReleaseByteArrayElements(barr, ba, 0);
        return size;
}

jbyteArray MyJniConvertUtils::toJbyteArray(JNIEnv* env, const char* charsData, const int length) {
        jbyteArray data = env->NewByteArray(length);
        env->SetByteArrayRegion(data, 0, length, (jbyte*)charsData);
        env->ReleaseByteArrayElements(data, env->GetByteArrayElements(data, JNI_FALSE), 0);
        return data;
}


std::string MyJniConvertUtils::getProperty(JNIEnv* env, std::string key) {
        jclass systemClazz = env->FindClass("java/lang/System");
        jmethodID mid = env->GetStaticMethodID(systemClazz, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
        jobject obj = env->CallStaticObjectMethod(systemClazz, mid, MyJniConvertUtils::toJString(env, key));
        jstring result = (jstring)obj;
        return MyJniConvertUtils::toString(env, result);
}

std::string MyJniConvertUtils::toString(const char* chars, const int length) {
        std::string data(chars, length);
        return data;
}

std::string MyJniConvertUtils::toString(JNIEnv* env, jstring param) {
        char* chars;
        int length = MyJniConvertUtils::toCharArray(env, param, chars);
        std::string stemp(chars, length);
        delete[] chars;
        return stemp;
}

公钥加密+私钥方法

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <iostream>
#include <cassert>
#include "openssl/rsa.h"
#include "openssl/pem.h"
#include "openssl/err.h"
#include "openssl/bio.h"
#include <string.h>

using namespace std;

/**
* 公钥加密
**/
int EncryptWithPublicKey(std::string pubKey, const char* text, const int length, char*& output) {
        std::string encrypt_text;
        BIO* keybio = BIO_new_mem_buf((unsigned char*)pubKey.c_str(), -1);
        RSA* rsa = RSA_new();
        rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);

        // 获取RSA单次可以处理的数据块的最大长度
        int key_len = RSA_size(rsa);
        // 因为填充方式为RSA_PKCS1_PADDING, 所以要在key_len基础上减去11
        int block_len = key_len - 11;

        // 申请内存:存贮加密后的密文数据
        int len = key_len + 1;
        char* sub_text = new char[len];
        memset(sub_text, 0, len);
        char* textCs = new char[block_len + 1];
        int ret = 0;
        int pos = 0;
        int left = length;
        int flagLength = 0;
        bool error = false;
        int lenText = 0;
        while (pos < length) {
                if (left >= block_len) {
                        flagLength = block_len;
                }
                else {
                        flagLength = left;
                }
                left -= flagLength;
                memset(textCs, 0, block_len + 1);
                memcpy(textCs, text + pos, flagLength);
                memset(sub_text, 0, len);
                ret = RSA_public_encrypt(flagLength, (const unsigned char*)textCs, (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
                if (ret >= 0) {
                        encrypt_text.append(std::string(sub_text, ret));
                        lenText += ret;
                }
                else {
                        error = true;
                        break;
                }
                pos += block_len;
        }
        // 释放内存  
        BIO_free_all(keybio);
        RSA_free(rsa);
        delete[] sub_text;
        delete[] textCs;
        if (!error) {
                const char* chars = encrypt_text.c_str();
                int len = lenText + 1;
                output = new char[len];
                memset(output, 0, len);
                memcpy(output, chars, len - 1);
                return len-1;
        }
        else {
                output = new char[1];
                output[0] = 0;
                return 0;
        }
}

/**
* 私钥解密
**/
int DecryptByPrivateKey(std::string priKey, const char* encryptText, const int length, char*& output) {
        std::string decrypt_text;
        RSA* rsa = RSA_new();
        BIO* keybio;
        keybio = BIO_new_mem_buf((unsigned char*)priKey.c_str(), -1);
        rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
        if (rsa == nullptr) {
                return 0;
        }
        int key_len = RSA_size(rsa);
        int len = key_len + 1;
        char* sub_text = new char[len];
        memset(sub_text, 0, len);
        char* eTextCs = new char[key_len];

        int ret = 0;
        int pos = 0;
        int count = 0;
        bool error = false;
        int left = length;
        int flagLength = 0;
        int textLength = 0;
        // 对密文进行分段解密
        while (pos < length) {
                if (left >= key_len) {
                        flagLength = key_len;
                }
                else {
                        flagLength = left;
                }
                left -= flagLength;
                memset(eTextCs, 0, key_len);
                memcpy(eTextCs, (encryptText + pos), flagLength);
                //sub_str = input.substr(pos, key_len);
                memset(sub_text, 0, len);
                ret = RSA_private_decrypt(flagLength, (const unsigned char*)eTextCs, (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
                if (ret >= 0) {
                        decrypt_text.append(std::string(sub_text, ret));
                        textLength += ret;
                        pos += key_len;
                }
                else {
                        error = true;
                        break;
                }
        }
        // 释放内存  
        delete[] sub_text;
        delete[] eTextCs;
        BIO_free_all(keybio);
        RSA_free(rsa);
        if (error) {
                return 0;
        }
        textLength++;
        output = new char[textLength];
        memset(output, 0, textLength);
        memcpy(output, decrypt_text.c_str(), textLength - 1);
        return textLength-1;
}

这里有个有趣的东西,就是RSA_private_decrypt这个方法,会有返回-1的情况,这样就是一种异常的情况。一般来说,我之前遇到的就是因为加密出来的私钥是包含’\0’字符,所以如果直接用str.substr()就会得到非预期的正常结果(其实说到底就是我对c++不熟悉,毕竟我都是玩java为主)。

JNI调用

上面的方法都准备好了,java对象转c++对象/类型的工具类也准备好了,然后就可以完善下前面的MyJni.java类了。这里就不做过多的解释了。
至于为啥我要传个int length类型,在说RSA_private_decrypt的时候也提了下。

编译构建+调用

最后,一切准备完毕,代码没啥错误就开始编译 ctrl+shift+B
这里记得把你的dll文件覆盖之前在C:\Windows\System32里面的(其实就是你放到的java.library.path上门的)

Linux环境下编译

我们的java服务程序,一般都是放到linux环境下面去跑的。所以,后面还有一篇,就是关于在centos下面如何编译我们自己的c++文件的一个简单说明
免费机票 Jni:使用openssl库进行rsa加密解密(Linux编译篇)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Native Interface(JNI)是Java平台的一项技术,它允许Java代码与本地代码(如C、C++进行交互。在这种情况下,我们可以使用JNI实现Base64加密和解密。 以下是一个示例代码,演示如何使用JNI实现Base64加密和解密: 1.创建一个名为"Base64.c"的C文件,其中包含以下代码: #include<jni.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<android/log.h> #include "android/bitmap.h" #include "android/log.h" #include "libjpeg/jpeglib.h" #include "com_example_bitmaptest_utils_CImageUtils.h" //Base64加密函数 JNIEXPORT jstring JNICALL Java_com_example_bitmaptest_utils_CImageUtils_encrypt(JNIEnv *env, jobject obj, jstring str) { const char *src = (*env)->GetStringUTFChars(env, str, NULL); if(src == NULL){ return NULL; } jsize len = (*env)->GetStringUTFLength(env, str); int mod = len % 3; int new_len = len + (mod == 0 ? 0 : (3 - mod)); unsigned char *input = (unsigned char *)malloc(new_len); memset(input, 0, new_len); memcpy(input, src, len); int i = 0, j = 0; unsigned char *output = (unsigned char *)malloc(new_len * 4 / 3); memset(output, 0, new_len * 4 / 3); while(i < new_len){ int c = input[i++] << 16 | input[i++] << 8 | input[i++]; output[j++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c >> 18]; output[j++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(c >> 12) & 0x3f]; output[j++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(c >> 6) & 0x3f]; output[j++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f]; } if(mod > 0){ output[new_len * 4 / 3 - 1] = '='; } if(mod == 1){ output[new_len * 4 / 3 - 2] = '='; } (*env)->ReleaseStringUTFChars(env, str, src); jstring result = (*env)->NewStringUTF(env, output); free(input); free(output); return result; } //Base64解密函数 JNIEXPORT jstring JNICALL Java_com_example_bitmaptest_utils_CImageUtils_decrypt(JNIEnv *env, jobject obj, jstring str) { const char *src = (*env)->GetStringUTFChars(env, str, NULL); if(src == NULL){ return NULL; } jsize len = (*env)->GetStringUTFLength(env, str); int new_len = len / 4 * 3; if(src[len - 1] == '='){ new_len--; } if(src[len - 2] == '='){ new_len--; } unsigned char *input = (unsigned char *)malloc(len); memset(input, 0, len); memcpy(input, src, len); int i = 0, j = 0; unsigned char *output = (unsigned char *)malloc(new_len); memset(output, 0, new_len); while(i < len){ unsigned char c[4]; int k = 0; while(k < 4 && i < len){ if(input[i] != '\n' && input[i] != '\r'){ c[k++] = input[i++]; }else{ i++; } } if(k == 4){ output[j++] = (c[0] << 2) | (c[1] >> 4); output[j++] = (c[1] << 4) | (c[2] >> 2); output[j++] = (c[2] << 6) | c[3]; } } (*env)->ReleaseStringUTFChars(env, str, src); jstring result = (*env)->NewStringUTF(env, output); free(input); free(output); return result; } 2.使用javac编译CImageUtils.java文件,生成CImageUtils.class文件。 3.使用javah命令生成.h头文件,命令为:javah -classpath . com.example.bitmaptest.utils.CImageUtils 4.将生成的CImageUtils.h文件拷贝到C文件所在目录下,并在C文件中包含该头文件。 5.使用ndk-build命令编译C文件,生成动态文件。 6.将生成的动态文件拷贝到Android项目的libs目录下。 7.在Java代码中调用JNI函数,例如: public class MainActivity extends AppCompatActivity { static { System.loadLibrary("base64"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String str = "hello, world"; String encryptStr = CImageUtils.encrypt(str); Log.i("MainActivity", "encryptStr: " + encryptStr); String decryptStr = CImageUtils.decrypt(encryptStr); Log.i("MainActivity", "decryptStr: " + decryptStr); } } 通过以上步骤,我们就可以在Android项目中使用JNI实现Base64加密和解密。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值