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;
}
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.
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.
来源:https://stackoverflow.com/questions/13351787/android-jni-string-encryption-decryption