Android的JNI字符串加密/解密 [英] Android JNI string encryption/decryption
问题描述
我试图做的AES加密/解密在本地code C.加密的工作,但,当我尝试对字符串进行解密。它不会最终成为原始字符串。下面是JNI方法,它不基于模式的参数加密/ decrpt:
jbyteArray Java_com_example_hellojni_HelloJni_encrypt(JNIEnv的* ENV,
jobject这一点,
jbyteArray srcData,
jint模式)
{
//获取字节长度
INT srcLen =(* ENV) - GT; GetArrayLength(ENV,srcData); //转换jbyteArray为byte []
jbyte数据[srcLen]
(* ENV) - > GetByteArrayRegion(ENV,srcData,0,srcLen,数据);
(* ENV) - > ReleaseByteArrayElements(ENV,srcData,数据,0);
无符号字符* INDATA =(无符号字符*)的数据;
const的无符号字符或u [] = {'H','A','R','D','C','O','D','E','D','','K' ,E,Y,1,2,3};
无符号字符* outdata = NULL;
outdata =的malloc(srcLen);
AES_KEY关键;
memset的(安培;键,0,sizeof的(AES_KEY)); 如果(==模式AES_ENCRYPT)
AES_set_encrypt_key(u键,128,和放大器;键);
其他
AES_set_decrypt_key(u键,128,和放大器;键); AES_ecb_encrypt(INDATA,outdata,和放大器;键,模式); jbyteArray bArray =(* ENV) - GT; NewByteArray(ENV,srcLen);
jboolean isCopy;
无效* decrypteddata =(* ENV) - GT; GetPrimitiveArrayCritical(ENV(jarray)bArray,&安培; isCopy);
的memcpy(decrypteddata,outdata,srcLen); (* ENV) - > ReleasePrimitiveArrayCritical(ENV,bArray,decrypteddata,0); 返回bArray;
}
任何想法,为什么解密加密字节是不一样的原始
由于建议的科多和owlstead我想仍然有同样的问题,更高层次的实现。
下面是saju.net.in/$c$c/misc/openssl_aes.c.txt
在code / **
*创建一个256位的密钥和IV使用提供的key_data。盐可以添加味道。
*在加密和解密CTX对象和填充成功返回0
** /
INT aes_init(无符号字符* key_data,INT key_data_len,unsigned char型*盐,EVP_CIPHER_CTX * E_CTX,
EVP_CIPHER_CTX * d_ctx)
{
INT I,nrounds = 5;
无符号的字符键[32],IV [32]; / *
*根键和放大器; IV为AES 256 CBC模式。当选择SHA1摘要是用于散列提供的关键材料。
* nrounds是次数我们散列材料。更多轮更安全,但
* 慢点。
* /
I = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),盐,key_data,key_data_len,nrounds,钥匙,IV);
如果(我!= 32){
的printf(密钥大小为%d位 - 应该是256位的\\ n,I);
返回-1;
} EVP_CIPHER_CTX_init(E_CTX);
EVP_EncryptInit_ex(E_CTX,EVP_aes_256_cbc(),NULL,键,IV);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx,EVP_aes_256_cbc(),NULL,键,IV); 返回0;
}/ *
* *加密len个字节数据
*所有数据将在&放大器;出被认为是二进制(无符号字符[])
* /
无符号字符* AES_ENCRYPT(EVP_CIPHER_CTX * E,无符号字符*明文,为int * LEN)
{
为len个明文的n字节/ *最大的密文为n + AES_BLOCK_SIZE -1个字节* /
INT c_len = * LEN + AES_BLOCK_SIZE,f_len = 0;
无符号字符*密文=的malloc(c_len); / *允许'E'的重用多个加密周期* /
EVP_EncryptInit_ex(即,NULL,NULL,NULL,NULL); / *更新密文,c_len填充有生成密文的长度,
* len为以字节为单位明文的大小* /
EVP_EncryptUpdate(即密文,和放大器; c_len,明文,* LEN); / *更新密文与最后剩余的字节* /
EVP_EncryptFinal_ex(即密文+ c_len,&安培; f_len); * LEN = c_len + f_len;
返回密文;
}/ *
*解密密文* len个字节
* /
无符号字符* AES_DECRYPT(EVP_CIPHER_CTX * E,const的无符号字符*密文,为int * LEN)
{
/ *因为我们有填充,我们必须分配的内存额外的密码块大小* /
INT p_len = * LEN,f_len = 0;
无符号字符*明文=的malloc(p_len + AES_BLOCK_SIZE); EVP_DecryptInit_ex(即,NULL,NULL,NULL,NULL);
EVP_DecryptUpdate(即明文,&安培; p_len,密文,* LEN);
EVP_DecryptFinal_ex(即明文+ p_len,&安培; f_len); * LEN = p_len + f_len;
返回明文;
}
这里是我的方法是所谓的java形式:
/ *的libcrypto不透明的加密,解密CTX结构用于记录
ENC的状态/ DEC操作* /
EVP_CIPHER_CTX恩,德;
jint
Java_com_example_hellojni_HelloJni_aesinit(JNIEnv的* ENV,
jobject OBJ)
{
无符号整型盐[] = {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); / *根密钥和IV。初始化密码CTX对象* /
如果(aes_init(key_data,key_data_len,(无符号字符*)及盐和放大器;恩,和放大器; DE)){
的printf(无法初始化AES密码\\ n);
__android_log_print(ANDROID_LOG_VERBOSE,APPNAME初始化失败AES);
返回0;
}
__android_log_print(ANDROID_LOG_VERBOSE,APPNAME初始化AES成功); 返回1;
}
jint
Java_com_example_hellojni_HelloJni_aesCleanup(JNIEnv的* ENV,
jobject OBJ)
{
EVP_CIPHER_CTX_cleanup(安培;连接);
EVP_CIPHER_CTX_cleanup(安培;去);
返回1;
} jbyteArray
Java_com_example_hellojni_HelloJni_encrypt(JNIEnv的* ENV,
jobject OBJ,的jstring textToEncrypt)
{ 为const char *明文=(* ENV) - GT; GetStringUTFChars(ENV,textToEncrypt,0);
INT LEN = strlen的(明文)+1;
无符号字符*密文= AES_ENCRYPT(安培;恩,(无符号字符*)明文和放大器; LEN); jbyteArray的字节数组=(* ENV) - GT; NewByteArray(ENV,strlen的(密文));
(* ENV) - GT; SetByteArrayRegion(ENV,字节,0,strlen的(密文),(常量jbyte *)密文); (* ENV) - GT; ReleaseStringUTFChars(ENV,textToEncrypt,明文);
返回的字节数组; } jbyteArray
Java_com_example_hellojni_HelloJni_decrypt(JNIEnv的* ENV,
jobject OBJ,的jstring textToDecrypt)
{
const的无符号字符*密文=(* ENV) - GT; GetStringUTFChars(ENV,textToDecrypt,NULL);
INT LEN = strlen的(密文)+1;
字符*明文=(字符*)AES_DECRYPT(安培;德,密文,和放大器; LEN);
jbyteArray的字节数组=(* ENV) - GT; NewByteArray(ENV,strlen的(明文));
(* ENV) - GT; SetByteArrayRegion(ENV,字节,0,strlen的(明文),(常量jbyte *)明文); (* ENV) - GT; ReleaseStringUTFChars(ENV,textToDecrypt,密文);
返回的字节数组;}
您提供的密钥为72位长(9个字符×8位)。但它需要128位多头(如你在调用 AES_set_encrypt_key
指定)。因此,缺少56位将或多或少随机的(这取决于接下来会发生什么的或u
阵列)。
要解决这个问题,指定一个较长的键或用0填充剩余的字节。
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.
这篇关于Android的JNI字符串加密/解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!