在 Android 上通过 jni 将 byte[] 传递到 C 时出现问题
我在 Java 中有一个 byte[]
,它报告其长度为 256 个字节,我将其传递给 C 中的本机函数。
当我尝试从该数组中获取数据时,这是完全错误的,当我打印出来它与我在将其传递给 C 之前打印出来的数据不匹配。
我尝试了几种访问数据的方法,包括 GetByteArrayRegion
和 GetByteArrayElements
但没有任何结果似乎给了我我期望的数据。
当我调查这个问题时,我尝试查看 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;
我错过了什么吗?
谢谢
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
此函数原型不正确:
第二个参数是
jclass
或jobject
。如果您的方法是静态的,则应该是:如果它不是静态的:
您将类或对象视为数组,这解释了您得到的意外结果。
This function prototype is incorrect:
The second argument is either a
jclass
or ajobject
. If your method is static, it should be:And if it's not static:
You are treating the class or object as an array, which explains the unexpected results that you get.
我认为主要问题是您将 OpenSSL 结构强制转换为字节数组。随着时间的推移,这个结构很可能会被释放。这可以解释当您返回到 C 时向您报告的奇怪且不同的长度。向 Java 生成
RSA*
也不会对您有太大帮助 - Java 不了解该特定结构和d将无法识别它。您应该尝试使用
取决于您是否只想将公钥信息或私有信息传递给 Java (另请参阅此处)。这样您就可以确保从一开始就处理字节数组。
一旦这对您有用(使用您已经尝试过的技术),回到 Java 中您就可以将字节数组解析为有意义的内容。这在公钥情况下很简单:使用 X509EncodedKeySpec 与您的数组并使用 KeyFactory#generatePublic 。
私钥的情况稍微复杂一些。 Java 仅理解 PKCS#8 格式 而 OpenSSL 默认情况下根据 PKCS#1 格式对其 RSA 私钥进行编码。但您已经可以使用 i2d_PKCS8PrivateKey_bio 将密钥转换为 PKCS#8。不过,您需要首先将
RSA*
包装为EVP_PKEY*
:请勿加密您的密钥并使用 内存中
BIO
,然后将生成的字节数组传递给 Java 并传递给构造函数PKCS8EncodedKeySpec
并最终使用KeyFactory
生成您的私钥。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
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 anEVP_PKEY*
first, though: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 ofPKCS8EncodedKeySpec
and finally generate your private key with theKeyFactory
.尝试在字符串后附加“\0”字符。可能它无法识别字符串的结尾。
Try appending the string with '\0' character. Probably its not able to identify the end of string.