在Android上将byte []通过jni传递给C的问题

时间:2011-08-13 08:44:55

标签: java android c java-native-interface openssl

我在Java中有一个byte[],它将其长度报告为256字节,我将其传递给C中的本机函数。

当我试图从这个数组中取出数据时,这是完全错误的,当我将其打印出来时,它与我在传递给C之前打印出的数据不匹配。

我尝试了几种方法来访问数据,包括GetByteArrayRegionGetByteArrayElements,但似乎没有任何方法可以提供我期望的数据。

当我调查这个时,我试着看看JNI认为jbyteArray的长度与GetArrayLength有什么关系 - 它报告的长度为1079142960,远远超过我预期的256个字节。每次调用函数时,值也不同,例如,另一个时间GetArrayLength返回1079145720。

以下是我用来访问数组的代码:

JNIEXPORT jbyteArray function(JNIEnv* env, jbyteArray array) {
    int length = (*env)->GetArrayLength(env, array);

    jbyte data[256];

    (*env)->GetByteArrayRegion(env, array, 0, 256, data);
    //also tried
    //jbyte *data = (jbyte*) (*env)->GetByteArrayElements(env, array, NULL);
}

这看起来非常简单,所以我不确定发生了什么。 Java中的数组看起来很好,但它是在C中生成并传回来的,所以我想在Java不关心的情况下可能会出现问题,但在返回C时会破坏数组。

以下是我用来生成数组并将其传递回Java的代码:

//there is some openSSL stuff here that sets up a pointer to an RSA struct called keys that is size bytes large

jbyteArray result = (*env)->NewByteArray(env, size);

(*env)->SetByteArrayRegion(env, result, 0, size, (jbyte*)keys;

我错过了什么吗?

由于

3 个答案:

答案 0 :(得分:12)

此函数原型不正确:

JNIEXPORT jbyteArray function(JNIEnv* env, jbyteArray array)

第二个参数是jclassjobject。如果您的方法是静态的,那么它应该是:

JNIEXPORT jbyteArray function(JNIEnv* env, jclass cls, jbyteArray array)

如果它不是静态的:

JNIEXPORT jbyteArray function(JNIEnv* env, jobject obj, jbyteArray array)

您将类或对象视为数组,这可以解释您获得的意外结果。

答案 1 :(得分:1)

我认为主要问题是你将OpenSSL结构强制转换为字节数组。很可能这个结构会随着时间的推移而被释放。这可以解释当你返回C时向你报告的奇怪和不同的长度。给Java一个RSA*也不会对你有所帮助 - Java不知道那个特定的结构并且不会是能够识别它。

你应该尝试使用

中的一个
  • i2d_PKCS8PrivateKey_bio(BIO * bp,EVP_PKEY * x,const EVP_CIPHER * enc,char * kstr,int klen,pem_password_cb * cb,void * u)
  • int i2d_RSA_PUBKEY(RSA * a,unsigned char ** pp)

取决于您是要将公钥信息还是私有信息传递给Java(see also here)。这样你就可以确保从一开始就处理一个字节数组。

一旦这对你有用(使用你已经尝试过的技术),在Java中你可以将字节数组解析成有意义的东西。这在公钥案例中是直截了当的:在您的数组中使用X509EncodedKeySpec并使用KeyFactory#generatePublic生成公钥。

私钥中的情况稍微复杂一些。 Java只能理解PKCS#8 format,而OpenSSL默认根据PKCS#1格式对其私有RSA密钥进行编码。但您可以使用i2d_PKCS8PrivateKey_bio将密钥转换为PKCS#8。您需要首先将RSA*包裹为EVP_PKEY*

EVP_pkey *pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);

不要加密密钥并使用in-memory BIO,然后将生成的字节数组传递给Java,然后传递给PKCS8EncodedKeySpec的构造函数,最后使用KeyFactory生成私钥

答案 2 :(得分:0)

尝试使用'\ 0'字符附加字符串。可能它无法识别字符串的结尾。