2

I am trying to do aes encryption/decryption in native code C. Encryption does work but when I try to decrypt the string. It doesn't end up as original string. Here is the JNI method which does encrypt/decrpt based on mode param:

jbyteArray Java_com_example_hellojni_HelloJni_encrypt( JNIEnv*  env,
                                      jobject  this,
                                      jbyteArray  srcData,
                                      jint mode)
{
  // get length of bytes
  int srcLen=(*env)->GetArrayLength(env,srcData);

  //convert jbyteArray to byte []
  jbyte data[srcLen];
  (*env)->GetByteArrayRegion(env, srcData, 0, srcLen, data);
  (*env)->ReleaseByteArrayElements(env, srcData,data , 0);


  unsigned char* indata=(unsigned char*)data;
   const unsigned char ukey[] = { 'H','A','R','D','C','O','D','E','D',' ','K','E','Y','1','2','3'};
  unsigned char *outdata = NULL;
  outdata = malloc(srcLen);
  AES_KEY key;
  memset(&key, 0, sizeof(AES_KEY));

 if(mode == AES_ENCRYPT)
    AES_set_encrypt_key(ukey, 128, &key);
 else
    AES_set_decrypt_key(ukey, 128, &key);

 AES_ecb_encrypt(indata, outdata, &key, mode);

 jbyteArray bArray = (*env)->NewByteArray(env, srcLen);
 jboolean isCopy;
 void *decrypteddata = (*env)->GetPrimitiveArrayCritical(env, (jarray)bArray, &isCopy);
 memcpy(decrypteddata, outdata, srcLen);

 (*env)->ReleasePrimitiveArrayCritical(env, bArray, decrypteddata, 0);

 return bArray;
}

Any ideas why decrypting the encrypted bytes are not the same as the original?

As suggested by Codo and owlstead I tried higher level implementation which still has the same issue.

Here is the code from saju.net.in/code/misc/openssl_aes.c.txt

/**
* Create an 256 bit key and IV using the supplied key_data. salt can be added for taste.
* Fills in the encryption and decryption ctx objects and returns 0 on success
**/
int aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx,
         EVP_CIPHER_CTX *d_ctx)
 {
   int i, nrounds = 5;
    unsigned char key[32], iv[32];

   /*
   * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
   * nrounds is the number of times the we hash the material. More rounds are more secure but
   * slower.
   */
   i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
   if (i != 32) {
    printf("Key size is %d bits - should be 256 bits\n", i);
    return -1;
 }

 EVP_CIPHER_CTX_init(e_ctx);
 EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
 EVP_CIPHER_CTX_init(d_ctx);
 EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);

 return 0;
}

/*
* Encrypt *len bytes of data
* All data going in & out is considered binary (unsigned char[])
*/
unsigned char *aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
{
  /* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
  int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
  unsigned char *ciphertext = malloc(c_len);

  /* allows reusing of 'e' for multiple encryption cycles */
  EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);

  /* update ciphertext, c_len is filled with the length of ciphertext generated,
  *len is the size of plaintext in bytes */
  EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len);

  /* update ciphertext with the final remaining bytes */
  EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);

 *len = c_len + f_len;
 return ciphertext;
}

/*
* Decrypt *len bytes of ciphertext
*/
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, const unsigned char *ciphertext, int *len)
{
   /* because we have padding ON, we must allocate an extra cipher block size of memory */
   int p_len = *len, f_len = 0;
   unsigned char *plaintext = malloc(p_len + AES_BLOCK_SIZE);

   EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
   EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
   EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);

   *len = p_len + f_len;
   return plaintext;
}

here are my methods that are called form java:

 /* "opaque" encryption, decryption ctx structures that libcrypto uses to record
 status of enc/dec operations */
 EVP_CIPHER_CTX en, de;


 jint
 Java_com_example_hellojni_HelloJni_aesinit( JNIEnv* env,
                                              jobject obj)
 {
      unsigned int salt[] = {12345, 54321};
      unsigned char key_data[]={ 'G','X','8','j','E','r','0','4','o','6','P','C','+','I','E','+'};
  int key_data_len;

  key_data_len = strlen(key_data);

  /* gen key and iv. init the cipher ctx object */
   if (aes_init(key_data, key_data_len, (unsigned char *)&salt, &en, &de)) {
     printf("Couldn't initialize AES cipher\n");
     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "initializing aes failed");
     return 0;
   }
   __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "initializing aes success");

   return 1;
}


  jint
  Java_com_example_hellojni_HelloJni_aesCleanup( JNIEnv* env,
                                              jobject obj)
  {
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 1;
  }



  jbyteArray
  Java_com_example_hellojni_HelloJni_encrypt( JNIEnv* env,
                                              jobject obj, jstring  textToEncrypt)
  {

   const char *plainText = (*env)->GetStringUTFChars(env, textToEncrypt, 0);
   int len = strlen(plainText)+1;
   unsigned char *ciphertext = aes_encrypt(&en, (unsigned char *)plainText, &len);

       jbyteArray byteArray=(*env)->NewByteArray(env, strlen(ciphertext));
    (*env)->SetByteArrayRegion(env, byteArray, 0, strlen(ciphertext), (const jbyte*)ciphertext);

   (*env)->ReleaseStringUTFChars(env, textToEncrypt, plainText);


   return byteArray;

  }



  jbyteArray
  Java_com_example_hellojni_HelloJni_decrypt( JNIEnv* env,
                                              jobject obj, jstring  textToDecrypt)
  {


   const  unsigned char *cipherText = (*env)->GetStringUTFChars(env, textToDecrypt, NULL);
   int len = strlen(cipherText)+1;
   char *plainText = (char *)aes_decrypt(&de, cipherText, &len);
   jbyteArray byteArray=(*env)->NewByteArray(env, strlen(plainText));
  (*env)->SetByteArrayRegion(env, byteArray, 0, strlen(plainText), (const jbyte*)plainText);

   (*env)->ReleaseStringUTFChars(env, textToDecrypt, cipherText);


   return byteArray;

}
Saqib
  • 1,737
  • 2
  • 19
  • 30

2 Answers2

1

You provide a key that is 72 bits long (9 characters x 8 bits). But it needs to be 128 bit longs (as you specify in the call to AES_set_encrypt_key). Thus the missing 56 bits will be more or less random (depending on what's next to the ukey array).

To fix it, specified a longer key or fill the remaining bytes with 0s.

Codo
  • 75,595
  • 17
  • 168
  • 206
  • I changed the key to be 16 characters long but still the same result. Updated the code above – Saqib Nov 12 '12 at 21:50
  • Is your data a multiple of 16 bytes? – Codo Nov 12 '12 at 22:20
  • No its not. Data could be of any length, depending upon what string user type in. I am converting string to bytes in java code using getBytes() – Saqib Nov 12 '12 at 23:14
  • `AES_ecb_encrypt` requires that the data be a multiple of 16 bytes. If your data is not, then you'll need to pad it. The encrypted data will be a multiple of 16 bytes as well. You better switch to some higher level functions that take care of all this. It's just too easy to get it wrong if you have to do it yourself. – Codo Nov 13 '12 at 06:40
  • i have tried this implementation. saju.net.in/code/misc/openssl_aes.c.txt which works as a demo. I am trying to convert it into something where I can call encrypt and decrypt from java as separate methods. Still the same issue. I have added the changes above – Saqib Nov 13 '12 at 21:08
  • @Saqib: Your question now contains a lot of code. And it's not even the complete test case to reproduce it. I propose you first isolate whether the problem is related to the JNI part of the implementation or the OpenSSL part. If you have more information, open a new question with a reduced amount of code that is really relevant to your problem. Currently, nobody seems to be able to locate the cause. – Codo Nov 15 '12 at 06:32
  • Yes it was getting really complicated and I settled on doing the encryption/decryption in java code but key generation code is still in native C. So there is some level of security – Saqib Nov 15 '12 at 17:34
1

You are using the low level encryption modes of OpenSSL. Your troubles are likely to vanish if you use the higher level EVP_* methods, e.g. for AES/CBC mode encryption. See also this related question.

Community
  • 1
  • 1
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • As you suggested I used this implementation saju.net.in/code/misc/openssl_aes.c.txt but still can't figure out the proper way of doing it. Please see the edits above – Saqib Nov 14 '12 at 17:35