使用JNI将字符串从C发送到Java [英] Send string from C to Java using JNI

查看:69
本文介绍了使用JNI将字符串从C发送到Java的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用JNI将字符串从C发送到Java. 这是我的C代码:

static char* test_encrypt_ecb_verbose(char* plain_text_char, char* key_char, char** ciphertext)
    {
        uint8_t i, buf[64], buf2[64];
        uint8_t key[16];
        // 512bit text
        uint8_t plain_text[64];
        char outstr[64];
        memcpy(key,key_char,16) ;
        memcpy(plain_text, plain_text_char, 64);
        memset(buf, 0, 64);
        memset(buf2, 0, 64);

        // print text to encrypt, key and IV
        printf("ECB encrypt verbose:\n\n");
        printf("plain text:\n");
        for(i = (uint8_t) 0; i < (uint8_t) 4; ++i)
        {
            phex(plain_text + i * (uint8_t) 16);
        }
        printf("\n");

        printf("key:\n");
        phex(key);
        printf("\n");

        // print the resulting cipher as 4 x 16 byte strings
        printf("ciphertext:\n");
        for(i = 0; i < 4; ++i)
        {
            AES128_ECB_encrypt(plain_text + (i*16), key, buf+(i*16));
            //phex(buf + (i * 16));
            for (int j = 0; j < 16; ++j){
                printf("%c", (buf + (i * 16))[j]);
            }

            printf("\n");
        }

        printf("\n\n decrypted text:\n");
        for (i = 0; i < 4; ++i)
        {
            AES128_ECB_decrypt(buf + (i * 16), key, plain_text + (i * 16));
            phex(plain_text + (i * 16));
        }
        printf("\n\n\n");
        *ciphertext = malloc(64);
        memcpy(*ciphertext, buf, 64);
        return outstr;


JNIEXPORT jstring JNICALL Java_JniApp_test
      (JNIEnv *env, jobject obj, jstring inputstr, jstring keystr){
          const char *str;
          const char *kstr;
          char* encstr=NULL,estr;
          str = (*env)->GetStringUTFChars(env,inputstr,NULL);
          kstr = (*env)->GetStringUTFChars(env,keystr,NULL);
          estr = test_encrypt_ecb_verbose(str,kstr,&encstr);
          printf("---Start---\n");
          //int b = test(num);
          for (int j = 0; j < 64; ++j){
            printf("%c",encstr[j]);
          }
          //printf("test: %c", *encstr);
          return(*env)->NewStringUTF(env,encstr);
      }

当我在返回到java之前打印encstr时,我得到了:

)?Ñi‰ªGÀ6Btílt〜,²#úK23Ej•

但是当我将其发送到Java并打印时,我没有得到相同的字符串.

这是我的Java文件:

public class JniApp {
    public native String test(String inputstr, String keystr);
    public static void main(String args[]){
        JniApp ja = new JniApp();
        String j = ja.test("1234567890abffff0987654321fedcba1234567890abffff0987654321fedcba","fred789lk6n2q7b1");
         System.out.println("This is the cipher text\n"+j);
    }
    static
    {
        System.loadLibrary("editjnib");
    }
}

如何在Java中也获得相同的字符串?我有什么事做错了吗?

解决方案

从逻辑上讲,Java String是Java char的不可变序列,它们依次是无符号的16位整数,表示UTF-16代码单位.从逻辑上讲,JNI函数GetStringUTFChars()将这样的序列转码为JVM的内部字符串格式,该格式是一个空终止的字节序列,其中包含字符串Java chars的修改后的UTF-8"编码.

通过两次调用GetStringUTFChars()所做的工作没有天生的错误.实际上,对test_encrypt_ecb_verbose()的调用也没有天生的错误,但是您必须考虑结果的重要性问题.如果要将其视为字符序列,那么它们的编码是什么?当然,假设结果是有效的[修改后的] UTF-8是不合理的.

取决于系统的本机语言环境,生成的字节序列可能是也可能不是有效的编码字符字符串.特别是,如果默认编码为一字节编码(例如ISO-8859-15),则将其传递给printf()可能会得到合理的结果,但否则,例如,当默认编码为UTF-8时,尝试打印无效字节序列时,取决于终端驱动程序.

类似地,JNI NewStringUTF()函数期望字节序列包含字符序列的有效的经过修改的UTF-8编码的字节,但是同样,您没有提供一个.没有理由认为JNI所做的事情将以任何特定的方式与您的终端驱动程序所做的事情相关联,并且期望能够从生成的Java字符串中恢复编码的字节是不合理的希望,我想您可能想解密它.

我看到了两种主要选择:

  1. 将Java字符串快速编码为byte[]而不是另一个String.您已经完成了大部分工作,但是您需要更改本机方法的返回类型,并做一些工作以将结果包装到Java byte[]中,而不是尝试将其放入String中. /p>

  2. 执行字符编码而不是字节编码.要正确执行此操作,最好的选择是使用21位Unicode代码点而不是(修改的)UTF-8代码单元或UTF-16代码单元.这将意味着将来自GetStringChars()的字符或来自GetStringUTFChars()的修改后的UTF-8字节解码为代码点序列(可能以int数组的形式);使用那些作为输入的方式执行加密,以使结果每个字符不需要超过21位;然后将结果重新编码为UTF-16或修改后的UTF-8,然后再使用它们构建结果String.

I am trying to send string from C to Java using JNI. Here is my C code:

static char* test_encrypt_ecb_verbose(char* plain_text_char, char* key_char, char** ciphertext)
    {
        uint8_t i, buf[64], buf2[64];
        uint8_t key[16];
        // 512bit text
        uint8_t plain_text[64];
        char outstr[64];
        memcpy(key,key_char,16) ;
        memcpy(plain_text, plain_text_char, 64);
        memset(buf, 0, 64);
        memset(buf2, 0, 64);

        // print text to encrypt, key and IV
        printf("ECB encrypt verbose:\n\n");
        printf("plain text:\n");
        for(i = (uint8_t) 0; i < (uint8_t) 4; ++i)
        {
            phex(plain_text + i * (uint8_t) 16);
        }
        printf("\n");

        printf("key:\n");
        phex(key);
        printf("\n");

        // print the resulting cipher as 4 x 16 byte strings
        printf("ciphertext:\n");
        for(i = 0; i < 4; ++i)
        {
            AES128_ECB_encrypt(plain_text + (i*16), key, buf+(i*16));
            //phex(buf + (i * 16));
            for (int j = 0; j < 16; ++j){
                printf("%c", (buf + (i * 16))[j]);
            }

            printf("\n");
        }

        printf("\n\n decrypted text:\n");
        for (i = 0; i < 4; ++i)
        {
            AES128_ECB_decrypt(buf + (i * 16), key, plain_text + (i * 16));
            phex(plain_text + (i * 16));
        }
        printf("\n\n\n");
        *ciphertext = malloc(64);
        memcpy(*ciphertext, buf, 64);
        return outstr;


JNIEXPORT jstring JNICALL Java_JniApp_test
      (JNIEnv *env, jobject obj, jstring inputstr, jstring keystr){
          const char *str;
          const char *kstr;
          char* encstr=NULL,estr;
          str = (*env)->GetStringUTFChars(env,inputstr,NULL);
          kstr = (*env)->GetStringUTFChars(env,keystr,NULL);
          estr = test_encrypt_ecb_verbose(str,kstr,&encstr);
          printf("---Start---\n");
          //int b = test(num);
          for (int j = 0; j < 64; ++j){
            printf("%c",encstr[j]);
          }
          //printf("test: %c", *encstr);
          return(*env)->NewStringUTF(env,encstr);
      }

When I print encstr before return back to java.. I get this:

)Ñi‰ª GÀ6Btílt˜,²#úK23Ej•)Ñi‰ª GÀ6Btílt˜,²#úK23Ej•

But when I send this to Java and print, I dont get the same string.

Here is my Java file:

public class JniApp {
    public native String test(String inputstr, String keystr);
    public static void main(String args[]){
        JniApp ja = new JniApp();
        String j = ja.test("1234567890abffff0987654321fedcba1234567890abffff0987654321fedcba","fred789lk6n2q7b1");
         System.out.println("This is the cipher text\n"+j);
    }
    static
    {
        System.loadLibrary("editjnib");
    }
}

How can I get the same string in Java also? I there anything I am doing wrong?

解决方案

Java Strings are, logically, immutable sequences of Java chars, which in turn are unsigned, 16-bit integers representing UTF-16 code units. The JNI function GetStringUTFChars(), logically, transcodes such a sequence into the JVM's internal string format, which is a null-terminated sequence of bytes containing the "modified UTF-8" encoding of the Java chars of the string.

There's nothing inherently wrong with what you do up through the two calls to GetStringUTFChars(). In fact, there's nothing inherently wrong with the call to test_encrypt_ecb_verbose(), either, but you do have to consider the question of the significance of the result. If you want to treat it as a sequence of characters, then what is their encoding? Certainly it would be unreasonable to suppose that the result is valid [modified] UTF-8.

Depending on your system's native locale, the resulting byte sequence might or might not be a valid string of encoded characters. In particular, if the default encoding is a one-byte encoding such as ISO-8859-15 then you might get a reasonable gibberish result from passing it to printf(), but otherwise, such as when the default encoding is UTF-8, it's up to the terminal driver what to do with your invalid byte sequence when you try to print it.

Similarly, the JNI NewStringUTF() function expects the byte sequence to contain the bytes of a valid modified UTF-8 encoding of a character sequence, but again, you're not providing one. There is no reason to suppose that what JNI does with that will be correlated in any particular way with what your terminal driver did with it, and it would be unreasonably hopeful to expect to be able to recover the encoded bytes from the resulting Java String, which I suppose you might want to do to decrypt it.

I see two main alternatives:

  1. Expressly encode the Java String to a byte[] rather than to another String. You're already most of the way there, but you'll need to change your native method's return type, and do a bit of work to pack the result into a Java byte[] instead of trying to put it into a String.

  2. Perform a character-wise encoding instead of a byte-wise encoding. To do this properly, your best bet is to work in terms of 21-bit Unicode code points instead of either (modified) UTF-8 code units or UTF-16 code units. That would mean decoding either the chars from GetStringChars() or the modified UTF-8 bytes from GetStringUTFChars() into code point sequences (probably in the form of int arrays); performing your encryption using those as input, in such a way that the results do not require more than 21 bits per character; and then recoding the results into UTF-16 or modified UTF-8 before constructing the result String with them.

这篇关于使用JNI将字符串从C发送到Java的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆