简体   繁体   中英

Send string from C to Java using JNI

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 String s are, logically, immutable sequences of Java char s, 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM