【问题标题】:Send string from C to Java using JNI使用 JNI 将字符串从 C 发送到 Java
【发布时间】:2017-05-15 18:48:21
【问题描述】:

我正在尝试使用 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•)Ñ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 c string java-native-interface


【解决方案1】:

Java Strings 在逻辑上是 Java chars 的不可变序列,它们又是无符号的 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 位;然后将结果重新编码为 UTF-16 或修改后的 UTF-8,然后用它们构造结果String

【讨论】:

    猜你喜欢
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2012-10-06
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多