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

使用JNI将字符串从C发送到Java,java,c,string,java-native-interface,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[6

我正在尝试使用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中也获得相同的字符串?我有什么做错了吗?

Java
String
s在逻辑上是Java
char
s的不可变序列,而chars又是表示UTF-16代码单元的无符号16位整数。JNI函数
GetStringUTFChars()
从逻辑上将这样的序列转换成JVM的内部字符串格式,这是一个以空结尾的字节序列,包含字符串的Java
chars
的“修改的UTF-8”编码

通过对
GetStringUTFChars()
的两个调用,您所做的事情本身并没有什么问题。事实上,对于调用<代码> TestPuxTytBeCbVBBOSURE()/<代码>没有什么本质上的错误,但是你必须考虑结果的重要性问题。如果要将其视为一个字符序列,那么它们的编码是什么?当然,假设结果是有效的[modified]UTF-8是不合理的

根据系统的本地语言环境,生成的字节序列可能是或不是有效的编码字符字符串。特别是,如果默认编码是单字节编码,如ISO-8859-15,则通过将其传递到
printf()
,可能会得到合理的乱码结果,但其他情况下,如当默认编码为UTF-8时,则由终端驱动程序决定在尝试打印无效字节序列时如何处理

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

我看到两个主要的选择:

  • 明确地将Java字符串编码为
    字节[]
    ,而不是另一个字符串。您已经完成了大部分工作,但是您需要更改本机方法的返回类型,并做一些工作将结果打包到Java
    字节[]
    中,而不是尝试将其放入字符串中

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

  • 看看这里:这里的示例展示了如何来回发送字符串。享受JNI带来的乐趣!
    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");
        }
    }