使用JNI将字符串从C发送到Java

时间:2017-05-15 18:48:22

标签: java c string java-native-interface

我正在尝试使用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时我得到了这个:

  

)NI‰ªGÀ6Btílt~,²#úK23Ej•)NI‰ª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中获取相同的字符串?我有什么我做错了吗?

1 个答案:

答案 0 :(得分:1)

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[]而不是另一个字符串。你已经完成了大部分工作,但是你需要更改本机方法的返回类型,并做一些工作将结果打包到Java byte[]中,而不是试图将它放入一个字符串中

  2. 执行字符编码而不是字节编码。要做到这一点,最好的办法是使用21位Unicode代码点而不是(修改的)UTF-8代码单元或UTF-16代码单元。这意味着将GetStringChars()中的字符或来自GetStringUTFChars()的修改后的UTF-8字节解码为代码点序列(可能以int数组的形式);使用这些作为输入执行加密,结果每个字符的结果不需要超过21位;然后在使用它们构造结果String之前将结果重新编码为UTF-16或修改后的UTF-8。