【问题标题】:JNI DETECTED ERROR IN APPLICATION: use of invalid jobject when returning Array of String应用程序中检测到 JNI 错误:返回字符串数组时使用无效的作业
【发布时间】:2021-04-06 22:15:59
【问题描述】:

预期目标的描述

我正在尝试使用 JNI 在 Android/Kotlin 中实现 OpenSSL 生成的公钥/私钥对,以便对存储到云服务器的信息实现用户加密。我已经编译了所有 OpenSSL 源代码并正确生成了所有 .so 文件。

代码(C++)

使用 OpenSSL 的 C++ 代码如下所示。 CmakeLists.txt 和 NDK 配置工作正常。

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_eu_joober_ui_entry_SplashFragment_generateRSAKeyPair(JNIEnv *env, jobject thiz) {

    int             ret = 0;
    RSA             *r = nullptr;
    BIGNUM          *bne = nullptr;
    BIO             *bp_public = nullptr, *bp_private = nullptr;

    int             bits = 2048;
    unsigned long   e = RSA_F4;

    jstring public_key_text;
    jstring private_key_text;
    jobjectArray returnPair = env->NewObjectArray(2, env->FindClass("java/lang/String"),nullptr);

    // 1. generate rsa key
    bne = BN_new();
    ret = BN_set_word(bne,e);
    if(ret != 1){
        goto free_all;
    }

    r = RSA_new();
    ret = RSA_generate_key_ex(r, bits, bne, nullptr);
    if(ret != 1){
        goto free_all;
    }

    // 2. get public key
    bp_public = BIO_new(BIO_s_mem());
    ret = PEM_write_bio_RSAPublicKey(bp_public, r);
    BIO_get_mem_data(bp_public, &public_key_text);
    if(ret != 1){
        goto free_all;
    }

    // 3. get private key
    bp_private = BIO_new(BIO_s_mem());
    ret = PEM_write_bio_RSAPrivateKey(bp_private, r, nullptr, nullptr, 0, nullptr, nullptr);
    BIO_get_mem_data(bp_private, &private_key_text);

    // Check public and private keys were generated correctly
    __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Public key is: \n%s",public_key_text);
    __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Private key is: \n%s",private_key_text);

    // 4. free
    free_all:

    BIO_free_all(bp_public);
    BIO_free_all(bp_private);
    RSA_free(r);
    BN_free(bne);

    // 5. Return strings using jobjectArray
    if (ret == 1) {
        env->SetObjectArrayElement(returnPair, 0, public_key_text);
        env->SetObjectArrayElement(returnPair, 1, private_key_text);
        return returnPair;
    }
    else {
        return nullptr;
    }
}

错误

如果我检查 Android Logcat,公钥和私钥似乎都生成正确(根据 __android_log_print 输出),但是当调用 env->SetObjectArrayElement(returnPair, 0, public_key_text); 时应用程序崩溃并出现以下错误:

JNI DETECTED ERROR IN APPLICATION: use of invalid jobject

IDE (Android Studio) 不会抱怨任何错误,并且日志表明密钥对正在正确生成,但我不知道为什么密钥没有存储在 jobjectArray 正确。事实上,如果我只是简单地说:

env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF("Hello"));
env->SetObjectArrayElement(returnPair, 1, env->NewStringUTF("World"));

代码运行良好,我的 Kotlin 代码正确获取字符串("Hello""World"),应用程序没有崩溃,这让我认为这个问题只是在 C++ 方面。

问题

我做错了什么?我检查了其他 SO 问题,例如 JNI converting jstring to char *jstring return in JNI program,稍作修改和组合,但没有运气。

旁注:我在 C++ 中使用 OpenSSL 实现,因为 Android/Kotlin 代码不提供使用 KeyPairGenerator.getInstance()generatePair() 生成的私钥(只能从 Keystore 检索公钥),我需要将其存储在不同的位置,以便即使卸载应用程序也可以检索用户信息,因为随后每次调用 generatePair() 都会导致不同的密钥对。如果您知道解决此问题的不同方法,我非常欢迎您提供任何建议。

【问题讨论】:

    标签: android c++ openssl android-ndk


    【解决方案1】:

    你从来没有从public_key_text创建一个java字符串

    试试

    char * public_key_text;
    ...
    BIO_get_mem_data(bp_public, &public_key_text);
    ...
    env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF(public_key_text));
    

    【讨论】:

    • 嗨@eyal-cinamon,感谢您的回答。如果我遵循您的建议,那么我会收到 Cannot initialize a parameter of type 'const char *' with an lvalue of type 'jstring' (aka '_jstring *') 错误。如果我遵循 Android Studio 的建议并将表达式转换为 const char*,然后更改为 env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF(reinterpret_cast<const char *>(public_key_text)));,则返回值为空 (""),这不是预期的结果 :(
    • 其实public_key_textprivate_key_text都是jstring对象,我认为它们是Java字符串的C表示,对吗?
    • 您使用未创建的 JSTRING OBJECT 作为 C++ 参数,这是错误的:(1) 因为未创建 jstring 变量,(2) 因为“BIO_get_mem_data()”需要 C++ 参数而不是 Java 对象。所以“jstring”在声明变量时变成了“char *”(一个C++“对象”),并且在数组中你必须放置JSTRING对象
    • @Franbede 您应该将private_key_textpublic_key_text 的类型更改为char* 而不是jstring(在我的示例中)。NewStringUTF() 将C 字符串转换为一个java字符串。
    • 感谢@EyalCinamon 和@emandt 的回复。抱歉,我错过了将 public_key_text 声明为 char *,现在它不会崩溃,而是在通过 env->NewStringUTF() 添加 public_key_text 时返回空字符串(“”)。我不明白为什么它没有正确返回值,但__android_log_print() 确实在 Android Studio Logcat 中显示了它们...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    相关资源
    最近更新 更多