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.
Related
When our app is getting a lot of traffic through JNI (hundreds of elements) seems like we are getting a lot of heap corruption errors (seems like it happens more for bigger elements).
abort 0x0000007e32cdf360
art::Runtime::Abort(char const*) 0x0000007daf4c22ac
android::base::LogMessage::~LogMessage() 0x0000007e33a6a654
art::gc::Verification::LogHeapCorruption(art::ObjPtr<art::mirror::Object>, art::MemberOffset, art::mirror::Object*, bool) const 0x0000007daf298318
art::gc::collector::ConcurrentCopying::MarkNonMoving(art::Thread*, art::mirror::Object*, art::mirror::Object*, art::MemberOffset) 0x0000007daf226b98
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::VisitRoots(art::mirror::CompressedReference<art::mirror::Object>**, unsigned long, art::RootInfo const&) 0x0000007daf22909c
art::Thread::HandleScopeVisitRoots(art::RootVisitor*, int) 0x0000007daf50af7c
void art::Thread::VisitRoots<false>(art::RootVisitor*) 0x0000007daf50e840
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*) 0x0000007daf22870c
art::(anonymous namespace)::CheckJNI::ReleasePrimitiveArrayElements(char const*, art::Primitive::Type, _JNIEnv*, _jarray*, void*, int) 0x0000007daf37c680
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt sodium-jni.c:156
art_quick_generic_jni_trampoline 0x0000007daf148354
<unknown> 0x000000009d05bbe8
Seems like the line causing the is located here (our code is open source) https://github.com/standardnotes/react-native-sodium/blob/367b61a90180fe75ddef5b599e01c47cb4761b1f/android/src/main/cpp/sodium-jni.c#L156. I've tried to debug this more but my JNI + CPP knowledge is limited. Do you have any tips for exchanging data from Java to C++ in a better way?
Code snippet:
JNIEXPORT jint JNICALL
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt(JNIEnv *jenv,
jclass clazz,
jbyteArray j_m,
jintArray j_mlen_p,
jbyteArray j_nsec,
jbyteArray j_c,
jint j_clen,
jbyteArray j_ad,
jint j_adlen,
jbyteArray j_npub,
jbyteArray j_k) {
unsigned char *c = as_unsigned_char_array(jenv, j_c);
unsigned char *m = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_m, 0);
unsigned char *npub = as_unsigned_char_array(jenv, j_npub);
unsigned char *ad = as_unsigned_char_array(jenv, j_ad);
unsigned char *nsec = as_unsigned_char_array(jenv, j_nsec);
unsigned char *k = as_unsigned_char_array(jenv, j_k);
int result = crypto_aead_xchacha20poly1305_ietf_decrypt(m, j_mlen_p, nsec, c, j_clen, ad, j_adlen, npub, k);
(*jenv)->ReleaseByteArrayElements(jenv, j_m, (jbyte *) m, 0);
return (jint)result;
}
Calling from java:
#ReactMethod
public void crypto_aead_xchacha20poly1305_ietf_decrypt(final String cipherText, final String public_nonce, final String key, final String additionalData, final Promise p) {
try {
byte[] c = this.base64ToBin(cipherText, Sodium.base64_variant_ORIGINAL());
byte[] npub = this.hexToBin(public_nonce);
byte[] k = this.hexToBin(key);
if (c == null || c.length <= 0)
p.reject(ESODIUM,ERR_FAILURE);
else if (npub.length != Sodium.crypto_aead_xchacha20poly1305_IETF_NPUBBYTES())
p.reject(ESODIUM,ERR_BAD_NONCE);
else if (k.length != Sodium.crypto_aead_xchacha20poly1305_IETF_KEYBYTES())
p.reject(ESODIUM,ERR_BAD_KEY);
else {
byte[] ad = additionalData != null ? additionalData.getBytes(StandardCharsets.UTF_8) : null;
int adlen = additionalData != null ? ad.length : 0;
int[] decrypted_len = new int[1];
byte[] decrypted = new byte[c.length - Sodium.crypto_aead_chacha20poly1305_IETF_ABYTES()];
int result = Sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, decrypted_len, null, c, c.length, ad, adlen, npub, k);
if (result != 0)
p.reject(ESODIUM,ERR_FAILURE);
else
p.resolve(new String(decrypted, StandardCharsets.UTF_8));
}
}
catch (Throwable t) {
p.reject(ESODIUM,ERR_FAILURE,t);
}
}
Seems like it happens for bigger elements most of the time, but not always. Also happens for crypto_1aead_1xchacha20poly1305_1ietf_1encrypt.
ReleasePrimitiveArrayElements means ->ReleaseByteArrayElements().
The issue is likely that you're referring to JNIEnv* and at some point it detaches from the thread (the processing time would be rather interesting). You'd need to obtain JNIEnv* differently, eg. alike AttachCurrentThreadIfNeeded(). Also see JNI threads.
I have a swig wrapper for jni # ndk.
The function header is:
//
// Created by Tomasz on 03/11/2017.
//
#ifndef PC_ANDORID_APP_RESIZE_GIF_H
#define PC_ANDORID_APP_RESIZE_GIF_H
int Version();
int ResizeAnimation(const char * infile, const char * outfile);
#endif //PC_ANDORID_APP_RESIZE_GIF_H
The swig interface is simple as this:
%module GifResizer
%inline %{
#include "resize-gif.h"
extern int Version();
extern int ResizeAnimation(const char * infile, const char * outfile);
%}
and the implementation of ResizeAnimation is:
int ResizeAnimation(const char * infile, const char * outfile) {
initialize();
/* ... */
return 0;
}
The problem is, that value of params in Swig generater wrapper:
SWIGEXPORT jint JNICALL Java_org_imagemagick_GifResizerJNI_ResizeAnimation(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2) {
jint jresult = 0 ;
char *arg1 = (char *) 0 ;
char *arg2 = (char *) 0 ;
int result;
(void)jenv;
(void)jcls;
arg1 = 0;
if (jarg1) {
arg1 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg1, 0);
if (!arg1) return 0;
}
arg2 = 0;
if (jarg2) {
arg2 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg2, 0);
if (!arg2) return 0;
}
result = (int)ResizeAnimation((char const *)arg1,(char const *)arg2);
jresult = (jint)result;
if (arg1) (*jenv)->ReleaseStringUTFChars(jenv, jarg1, (const char *)arg1);
if (arg2) (*jenv)->ReleaseStringUTFChars(jenv, jarg2, (const char *)arg2);
return jresult;
}
is okay and the arg1 and arg2 have proper values, but once ResizeAnimation is called, the pointers point to different memory address, and infile (arg1) is null, while outfile (arg2) is some random memory.
All the sources are built with standard android CMake for NDK.
The problem was caused by running x86_64 code on x86 emulator. Silly :)
I have been trying to replicate an android code which does AES256 encryption using md5 doubled as the key. Everything seems to be fine but the values after the encryption doesn't seem to be the same. Please go through my below codes
Android :-
public static String encrypt(String key, String value) {
try {
byte[] keyArr = new byte[32];
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(key.getBytes("US-ASCII"));//in md5 function 1st line
keyArr = arrayCopy(0, hash, 0, keyArr, 16);//in md5 function 1st for loop
keyArr = arrayCopy(0, hash, 15, keyArr, 16);//in md5 function 2nd for loop
SecretKeySpec skeySpec = new SecretKeySpec(keyArr, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(value.getBytes());
String encryptedB64 = new String(Base64.encode(encrypted, Base64.DEFAULT));
return encryptedB64;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
private static byte[] arrayCopy(int sourceIndex,byte[] source,int targetIndex,byte[] target,int transferSize){
if(!(transferSize >0))
return null;
if(sourceIndex>=0 && sourceIndex < source.length){
int transferCnt=0;
int i=sourceIndex;
for(int j=targetIndex;;j++,i++){
if(targetIndex>=target.length || sourceIndex>=source.length || (++transferCnt>transferSize)){
break;
}
target[j] = source[i];
}
}else{
return null;
}
return target;
}
iOS objective-c
+ (NSString *) getFalconEncryptedValueForKey:(NSString *)theKey forString:(NSString *)theString
{
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
NSData *rawData = [theString dataUsingEncoding:NSUTF8StringEncoding];
NSString *md5Key = [self md5:theKey];
[md5Key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [rawData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding + kCCOptionECBMode,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[rawData bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess)
{
NSData *tempData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
NSString* encrypted64 = [tempData base64EncodedStringWithOptions:0];//Even i have tried base 64 encding with other options available
return encrypted64;
}
free(buffer); //free the buffer;
return nil;
}
+ (NSString *) md5:(NSString *) input
{
// const char * pointer = [self UTF8String];
const char *cStr = [input cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
NSMutableString *md5String = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[md5String appendFormat:#"%02x",result[i]];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[md5String appendFormat:#"%02x",result[i]];
return md5String;
//for 32 byte. md5 produces only 16 byte info. we are replicating it again to make it 32 byte for aes256
}
Any kind of direction in what I have done wrong will be really helpful. Thanks in advance
AES256 encryption using md5 doubled as the key
No, that's not the case.
In Android, the last byte of the first hash in keyArr is overwritten by the second hash (more precisely the first byte of the second hash). Therefore, the last byte of keyArr is always 0.
I'm not fluent in Objective-C, but I think, this should do it:
NSMutableString *md5String = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH-1; i++)
[md5String appendFormat:#"%02x",result[i]];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[md5String appendFormat:#"%02x",result[i]];
[md5String appendFormat:#"%02x",0];
(Btw, arrayCopy can produce an IndexOutOfBoundsException despite its numerous checks).
Of course, this produces a Hex-encoded string with a length of 64 characters. This is not what you want. Instead, you should produce actual bytes of length 32.
Correct code (by OP)
+ (NSString *) getEncryptedValueWithKey:(NSString *)theKey forString:(NSString *)theString
{
NSData *rawData = [theString dataUsingEncoding:NSUTF8StringEncoding];
unsigned char md5Buffer[kCCKeySizeAES256+1];
memset(md5Buffer, 0, kCCKeySizeAES256+1);
const char *cStr = [theKey UTF8String];
// do md5 hashing
CC_MD5(cStr, CC_MD5_DIGEST_LENGTH, md5Buffer);
unsigned char lastChar = md5Buffer[15];
for (NSInteger i = 15; i <= 30; i++) {
md5Buffer[i] = md5Buffer[i-15];
}
md5Buffer[30] = lastChar;
//MD5 key obtaining end
NSUInteger dataLength = [rawData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding + kCCOptionECBMode, md5Buffer, kCCKeySizeAES256, NULL /* initialization vector (optional) */, [rawData bytes], dataLength, /* input */ buffer, bufferSize, /* output */ &numBytesEncrypted);
if (cryptStatus == kCCSuccess)
{
NSData *tempData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
NSString* encrypted64 = [tempData base64EncodedStringWithOptions:0];//Even i have tried base 64 encding with other options available
return encrypted64;
}
free(buffer); //free the buffer;
return nil;
}
I have finished a AES encryption wrapper written in C using OpenSSL library. I used this wrapper in my Android project. When I called the encrypt function hundred of times to encrypt a lot of small files (images) it caused the crash error:
02-06 14:39:44.110: A/libc(5114): ### ABORTING: INVALID HEAP ADDRESS IN dlfree
02-06 14:39:44.110: A/libc(5114): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1)
This is memory leak error but I can't figure out myself. I guess something went wrong in my native code. Below is my encrypt function written in C using OpenSSL library. (Compiled by NDK)
Function init encrypt key
int aes_init_encrypt(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx)
{
int i, nrounds = 4;
unsigned char key[16], iv[16];
/*
* Gen key & IV for AES 128 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_128_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
if (i != 16)
{
//__android_log_write(ANDROID_LOG_ERROR, "TamNV-Encryption", "Key size is %d bits - should be 128 bits\n");
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_128_cbc(), NULL, key, iv);
return 0;
}
Function encrypt
jint Java_com_openssl_aes_WrapperAES_encryptAES(JNIEnv *env, jobject obj, jstring password, jstring source, jstring destination)
{
FILE* source_file;
FILE* destination_file;
const char *source_path = (*env)->GetStringUTFChars(env, source, NULL);
const char *destination_path = (*env)->GetStringUTFChars(env, destination, NULL);
source_file = fopen(source_path, "rb");
destination_file = fopen(destination_path, "wb");
if (source_file == NULL || destination_file == NULL) {
return -1;
}
// Prepare to encrypt
// 1. Get password
const char *pass = (*env)->GetStringUTFChars(env, password, NULL);
unsigned char *key_data = malloc(strlen(pass));
if (key_data)
strcpy(key_data, pass);
else
return -1;
// 2. Init EVP_CIPHER
EVP_CIPHER_CTX e_ctx;
if (aes_init_encrypt(key_data, strlen(key_data), key_data, &e_ctx) == -1)
return -1;
/* Buffers */
unsigned char inbuf[BUFFER_SIZE];
int inlen;
/* Allow enough space in output buffer for additional cipher block */
unsigned char outbuf[BUFFER_SIZE + AES_BLOCK_SIZE];
int outlen;
int writelen;
while ((inlen = fread(inbuf, 1, BUFFER_SIZE, source_file)) > 0) {
// fwrite(inbuf, 1, inlen, destination_file);
if (!EVP_CipherUpdate(&e_ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
writelen = fwrite(outbuf, sizeof(*outbuf), outlen, destination_file);
if (writelen != outlen) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
}
/* Handle remaining cipher block + padding */
if (!EVP_CipherFinal_ex(&e_ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
/* Write remainign cipher block + padding*/
fwrite(outbuf, sizeof(*inbuf), outlen, destination_file);
EVP_CIPHER_CTX_cleanup(&e_ctx);
fclose(source_file);
fclose(destination_file);
free(key_data);
// __android_log_print(ANDROID_LOG_DEBUG, "TamNV","Encrypt successfully --- %s", source_path);
return 0;
}
What's wrong with my native code? Any help will be much appreciated!
As you can see, I get jbyte *str form the utf string. Then each character of string has two jbytes else one byte?
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {
char buf[128];
const jbyte *str;
str = (env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; / OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than * 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
Here:
str = (env)->GetStringUTFChars(env, prompt, NULL);
you receive buffer of single byte chars. You may even edit this like here:
const char *str = (env)->GetStringUTFChars(env, prompt, NULL);
because GetStringUTFChars() is declared as returning const char *.