Home » Android » java – Problem passing byte[] via jni to C on Android

java – Problem passing byte[] via jni to C on Android

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have a byte[] in Java which reports its length as 256 bytes which I pass to a native function in C.

When I tried to get the data out of this array it was completely wrong and when I printed it out it did not match the data I printed out right before I passed it to C.

I tried a few ways to access the data including both GetByteArrayRegion and GetByteArrayElements but nothing seems to give me the data I expect.

As I was investigating this I tried to look at what JNI believed the jbyteArray‘s length was with GetArrayLength – it reported the length as 1079142960, far more than the 256 bytes I expected. Also the value was different each time the function was called, for example another time GetArrayLength returned 1079145720.

Here is the code I am using to access the array:

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);
}

This seems pretty straight forward so I’m not really sure what is going on. The array seems fine from Java but it was generated in C and passed back so I suppose something might have gone wrong there that Java doesn’t care about but breaks the array when it comes back to C.

Here is the code I used to generate the array and pass it back to 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;

Am I missing something?

Thanks

How to&Answers:

This function prototype is incorrect:

JNIEXPORT jbyteArray function(JNIEnv* env, jbyteArray array)

The second argument is either a jclass or a jobject. If your method is static, it should be:

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

And if it’s not static:

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

You are treating the class or object as an array, which explains the unexpected results that you get.

Answer:

I assume the main problem is that you force an OpenSSL struct into a byte array. Most likely this struct will get freed over time. That would explain the weird and differing lengths being reported back to you when you return back to C. Yielding an RSA* to Java will also not help you very much – Java has no knowledge about that particular struct andd won’t be able to recognize it.

What you should try is using one of

  • 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)

depending on whether you want to just pass the public key information or also the private information to Java (see also here). This way you can be sure to deal with a byte array right from the beginning.

Once this works for you (using the techniques you already tried), back in Java you can parse the byte array into something meaningful. This is straight-forward in the public key case: Use X509EncodedKeySpec with your array and generate a public key using KeyFactory#generatePublic.

Things are slightly more complicated in the private key case. Java only understands the PKCS#8 format whereas OpenSSL encodes its private RSA keys according to the PKCS#1 format by default. But you can already convert your key to PKCS#8 using i2d_PKCS8PrivateKey_bio. You need to wrap your RSA* as an EVP_PKEY* first, though:

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

Do not encrypt your key and use an in-memory BIO, then pass the resulting byte array over to Java and there to the constructor of PKCS8EncodedKeySpec and finally generate your private key with the KeyFactory.

Answer:

Try appending the string with ‘\0’ character. Probably its not able to identify the end of string.