How to write Cipher.getInstance("DES"); in jni? - android

I would like to change
Cipher cipher = Cipher.getInstance("DES");
into cpp code in jni.How to do it?

JNIEnv *jni; //Comes from somewhere
jclass cl = jni->FindClass("javax/crypto/Cipher");
jmethodID MID = jni->GetStaticMethodID(cl, "getInstance", "(Ljava/lang/String)Ljavax/crypto/Cipher;");
jstring s = jni->NewStringUTF("DES");
jobject cipher = jni->CallStaticObjectMethod(cl, MID, s);
That's omitting error handling.

Another alternative would be to use native code for encryption, e.g. OpenSSL.
Mandatory warning: don't use DES.

Related

Concatenating byte arrays in Jni android

I am new to JNI and trying to muddle my way through. Please can someone point me in the direction to do the following in JNI. I am a bit out of my depth here, Can JNI handle Byte Arrays. Also were is the best places to find JNI examples.
This is the JAVA code I want to convert
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(key1);
outputStream.write(key2);
byte[] key3 = outputStream.toByteArray();
Thank you in advance
Rob
Here is a literal translation of the Java code you posted:
jclass cls_BAOS = env->FindClass("java/io/ByteArrayOutputStream");
jmethodID ctr_BAOS = env->GetMethodID(cls_BAOS, "<init>", "()V");
jobject baos = env->NewObject(cls_BAOS, ctr_BAOS);
jmethodID mid_BAOS_writeBytes = env->GetMethodID(cls_BAOS, "writeBytes", "([B)V");
env->CallVoidMethod(baos, mid_BAOS_writeBytes, key1);
env->CallVoidMethod(baos, mid_BAOS_writeBytes, key2);
jmethodID mid_BAOS_toByteArray = env->GetMethodID(cls_BAOS, "toByteArray", "()[B");
jbyteArray key3 = (jbytearray)env->CallObjectMethod(baos, mid_BAOS_toByteArray);
And here is an implementation that uses lower-level operations:
jsize key1len = env->GetArrayLength(key1);
jsize key2len = env->GetArrayLength(key2);
jbyteArray key3 = env->NewByteArray(key1+key2);
{
jbyte *key1ptr = env->GetByteArrayElements(key1, nullptr);
env->SetByteArrayRegion(key3, 0, key1len, key1ptr);
env->ReleaseByteArrayElements(key1, key1ptr, JNI_ABORT);
}
{
jbyte *key2ptr = env->GetByteArrayElements(key2, nullptr);
env->SetByteArrayRegion(key3, key1len, key2len, key2ptr);
env->ReleaseByteArrayElements(key2, key2ptr, JNI_ABORT);
}
You could also memcpy both byte arrays to a single C++ array first and then use a single call to SetByteArrayRegion or anything else really, but what's the point? The Java code was simple and readable and all of this JNI code is just adding pointless obfuscation.
Thank you Botje, this is the final code that works.
jclass cls_BAOS = env->FindClass("java/io/ByteArrayOutputStream");
jmethodID ctr_BAOS = env->GetMethodID(cls_BAOS, "<init>", "()V");
jobject baos = env->NewObject(cls_BAOS,ctr_BAOS);
jmethodID mid_BAOS_writeBytes = env->GetMethodID(cls_BAOS, "writeBytes", "([B)V");
env->CallVoidMethod(baos, mid_BAOS_writeBytes, a);
env->CallVoidMethod(baos, mid_BAOS_writeBytes, b);
jmethodID mid_BAOS_toByteArray = env->GetMethodID(cls_BAOS, "toByteArray", "()[B");
jbyteArray key3 = (jbyteArray)env->CallObjectMethod(baos, mid_BAOS_toByteArray);

How to set IntField from native jni to Java Android 10

After update my project to support Android 10, a crash produced in the JNI level related to non-SDK interface restrictions in Android 10 :
JNI DETECTED ERROR IN APPLICATION: JNI SetIntField called with pending exception java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses
which come from this part of code :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jfieldID mi = (*env)->GetFieldID(env, clazz, "value", "I");
(*env)->SetIntField(env, outputObj, mi, pListLen);
To fix that i replace it by :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jmethodID intValueMethod = (*env)->GetMethodID(env, clazz, "intValue", "()I");
jint result = (*env)->CallIntMethod(env, outputObj,intValueMethod,pListLen);
After that my application doesn't crash and find the integer value correctly, but i want to set the result integer on Java code using the same method SetIntField.
Could you please give me a way or method to set the result on the Java part.
As changing the intger field is not a good practice on JNI part, so i changed the way instead changing it by reflection method
jclass clazz = (*env)->GetObjectClass(env, outputBufLen);
jmethodID value_of = (*env)->GetStaticMethodID(env,clazz, "valueOf", "(I)Ljava/lang/Integer;");
jobject result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
I set the result under an int Array and i return it :
int result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
jintArray resultArray = (*env)->NewIntArray(env, 2);
jint fill[2];
fill[0] = result;
fill[1] = (jint) pOutBufLen;
(*env)->SetIntArrayRegion(env, result, 0, 2, fill);
return (resultArray);

In JNI,Blowfish in C++ cannot encrypt/decrypt correctly

I tried to migrate the Blowfish algorithm to the Android Native which is implement on C++.
But there are some confusing for me!
I get everything OK when ecrypting, but it can not get a correctly decryption.
Following the code:
I do this in the JNI:
JNIEXPORT jstring JNICALL Java_com_myview_myndk_LoadNative_e(JNIEnv *env, jobject this_obj,jstring src) {
const string key = "abcfgetq";
vector<char> k(key.begin(), key.end());
Blowfish blowfish(k);
jboolean isCopy;
const char *src_char = env->GetStringUTFChars(src, &isCopy);
LOGI("jchar %s======================",src_char);
const vector<char> src_vector(src_char,src_char+strlen(src_char));
vector<char> encyyption = blowfish.Encrypt(src_vector);
char *en_char = &encyyption[0];
const string en_str(en_char);
string base64_en = base64::encode(en_str);
const char *base64_de_char = base64::decode(base64_en).c_str();
vector<char> decryption = blowfish.Decrypt(vector<char>(base64_de_char,base64_de_char+strlen(base64_de_char)));
char *de_char = &decryption[0];
string de_str(de_char);
//log decryption
LOGI("decryption is %s======================",de_str.c_str());
return env->NewStringUTF(base64_en.c_str());
}
I got the results like that:
Before encryption:
Hello!!How Are You?!//..,,++||**
While encrypted:
8�{}�a�-�#e�ՠ�ϡz�P]#S�/��P(**
After decryption:
Hello!!How Are You?!//..,,++||ԘJi**
What's wrong with that?
I've working at this about two days and I really confused about that!
If I missed something about you push a right answer, please tell me.
THANKS FOR YOUR intelligence!

JNI call to find Java class from native results in NoCLassDefFoundError

I am using following code to access a java class from native code
JNIEnv *env = nullptr;
JMVEnv::attachCurrentJNIENv(&env);
jclass jXYZClass = env->FindClass("com/xxx/xx/xx/XYZClassName");
This call passes 4-5 times but fails after that. jXYZClass is null after some calls.
So, while compilation class is found and during execution also it was found 4-5 times. I am calling this code from different locations. Can it be some threading issue?
I think it is a multhreading problems... Especially when you say that " it was found 4-5 times. I am calling this code from different locations".
See this post for more details.
Firstly, you can print the thread id of each thread to see if they are the same thread.
You can Findclass in JNI_OnLoad:
jobject g_class;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
JavaVM *gJvm = pjvm; // cache the JavaVM pointer
JNIEnv *env= NULL;
env = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
jclass tmp = env->FindClass("com/xxx/xx/xx/XYZClassName");
g_class = env->NewGlobalRef(tmp);
}
whenever you want to use this class:
// extern jobject g_class; Add this Line if this is in another cpp file
jmethodID methodID = env->GetMethodID((jclass)g_class, "<init>", "()V");
jobject new_object = env->NewObject((jclass)g_class, methodID);

JNI - transfer from jstring to byte, from byte to string issue

I have a problem with converting in JNI.
In C++ I'm creating some cipher using AES (Library CryptoPP). I'm converting result to string and returning it. This is how the code getting the string looks like:
JNIEXPORT jbyteArray JNICALL Java_com_example_androidake_MutualAuthenticateChip_prepareEncryptionCPP
(JNIEnv *env, jobject thisObj, jboolean hmm, jboolean jinit) {
string encryption= mac->EncryptCertKey();
jbyteArray returns = env->NewByteArray(encryption.size());
env->SetByteArrayRegion(returns, 0, encryption.length(), (jbyte*) encryption.c_str());
return returns;
};
Above string is converting to jbyteArray which is returned. First I wanted just return string using
env->NewStringUTF(encryption.c_str());
but the application has been crashing. I think it is caused by content of variable 'encryption'. I'm using env->NewStringUTF(encryption.c_str()); in other functions, where returned string is for example just a number or something like that.
Then in Java I'm doing conversion from byte to string:
byte[] cipher = mac_A.prepareEncryptionCPP(true, true);
string cipher_str = new String(cipher);
And I'm putting that string again to the C++ object and compare old cipher with the cipher which is sent from Java:
//Java
boolean result = mac_A.compareEncryption(true, cipher);
//JNI
JNIEXPORT jboolean JNICALL Java_com_example_androidake_MutualAuthenticateChip_compareEncryption
(JNIEnv * env, jobject thisObj, jboolean jinit, jstring cipher){
bool init = jinit;
bool result;
jsize length = env->GetStringUTFLength(cipher);
const char *inCStr_ek = env->GetStringUTFChars(cipher, 0);
string s(inCStr_ek, length);
result = mac->CompareCipher(s);
env->ReleaseStringUTFChars(cipher, inCStr_ek);
return result;
};
Comparing in C++ :
bool MyClass::CompareCipher(std::string cipher_2){
if(cipher == cipher_2){
return true;
}else{
return false;
}
}
And it always returns false. I do not know what I'm doing wrong. I've even sent this cipher from Java to C++ and take it back to Java and the strings are equals, but in C++ side are not.
on your java side code you have
byte[] cipher = mac_A.prepareEncryptionCPP(true, true);
boolean result = mac_A.compareEncryption(true, cipher);
compareEncryption jni function is defined with jstring , not jbytearray.
So from JNI side you are sending a byte array to java, and from java side you send back the same byte array to native side (but uses jstring in call), but then you are using env->GetStringUTFChars(cipher, 0) which converts that byte array into a modified UTF-8 string, so its technically not that same byte array anymore.
if you need strings do the conversions in java side, and just use with same plain byte arrays between jni and java. See this for string encoding issues in Android JNI.

Categories

Resources