简体   繁体   中英

Calling getWritableDatabase (SQLCipher java method) from JNI

I have a java code to open the SqlCipher database. I want to call a getWritableDatabase function from JNI to hide the password.

An error occurred when calling getWritableDatabas method from SQLiteOpenHelper class in Java_com_company_pos_DbHelper_getWritableSqlite method (sqlauth.cpp), what is wrong with my code?

DbHelper.java

public class DbHelper extends SQLiteOpenHelper {
    //private final static String PASSWORD= "MyPassword"
    private final static String TAG = "DbHelper";
    private static SQLiteDatabase db = null;

    /* This method works only password is not secure.
    public void open() {
        try {
            //The password isn't hidden
            db = getWritableDatabase(PASSWORD);
        } catch (SQLiteException e) {
            Log.e(TAG, e.getMessage());
        }
    }
    */

    static {
        System.loadLibrary("sqlauth");
    }

    private native SQLiteDatabase getWritableSqlite();

    public void open() {
        try {
            //The password is hidden in jNI
            db = getWritableSqlite();
        } catch (SQLiteException e) {
            Log.e(TAG, e.getMessage());
        }
    }

}

JNI

sqlauth.h

#include <jni.h>
#include <string.h>
#include <iostream>

#ifndef SQLAUTH_H
    #define SQLAUTH_H
#endif

static char *CLS_SQLITEOPENHELPER = (char *) "net/sqlcipher/database/SQLiteOpenHelper";
static char *MID_GETWRITABLEDATABASE = (char *) "getWritableDatabase";
static char *SIG_GETWRITABLEDATABASE = (char *) "(Ljava/lang/String;)Lnet/sqlcipher/database/SQLiteDatabase;";

#ifdef __cplusplus
    extern "C" {
#endif

JavaVM *_jvm;
jclass _clsSQLiteOpenHelper;
jobject _objGetWritableDatabase;
jmethodID _midGetWritableDatabase;

JNIEnv *getEnv();
jboolean tryCatch();
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved);
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved);
JNIEXPORT jobject JNICALL Java_com_company_pos_DbHelper_getWritableSqlite(JNIEnv *e, jobject obj);

#ifdef __cplusplus
    }
#endif

sqlauth.cpp

#include "sqlauth.h"

#ifdef __cplusplus
    extern "C" {
#endif

JNIEnv *getEnv() {
    JNIEnv *env;
    _jvm->GetEnv((void **) &env, JNI_VERSION_1_6);

    return env;
}

jboolean tryCatch() {
    JNIEnv *env = getEnv();
    if (env == NULL) {
        return JNI_TRUE;
    }

    jthrowable ex = env->ExceptionOccurred();
    if (ex) {
        env->ExceptionClear();
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env;
    jclass clsDbHelper, clsSqliteDatabase, clsSQLiteOpenHelper;

    _jvm = jvm;
    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6)) {
        return JNI_ERR;
    }

    // SQLiteOpenHelper.java
    clsSQLiteOpenHelper = env->FindClass(CLS_SQLITEOPENHELPER);
    if (clsSQLiteOpenHelper == NULL) {
        return JNI_ERR;
    }
    _clsSQLiteOpenHelper = (jclass) env->NewWeakGlobalRef(clsSQLiteOpenHelper);
    if (_clsSQLiteOpenHelper == NULL) {
        return JNI_ERR;
    }
    _midGetWritableDatabase = env->GetMethodID(_clsSQLiteOpenHelper, MID_GETWRITABLEDATABASE, SIG_GETWRITABLEDATABASE);
    if (_midGetWritableDatabase == NULL) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *jvm, void *reserved) {
    JNIEnv *env;

    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6)) {
        return;
    }
    env->DeleteWeakGlobalRef(_clsSQLiteOpenHelper);
    return;
}

JNIEXPORT jobject JNICALL
Java_com_company_pos_DbHelper_getWritableSqlite(JNIEnv *e, jobject obj) {
    JNIEnv *env = getEnv();
    if (env == NULL || obj == NULL) {
        return NULL;
    }

    // Error in here when calling getWritableDatabase 
    // from net/sqlcipher/database/SQLiteOpenHelper
    _objGetWritableDatabase = env->CallObjectMethod(_clsSQLiteOpenHelper, _midGetWritableDatabase, env->NewStringUTF("MyPassword"));  
    if (_objGetWritableDatabase == NULL || tryCatch()) {
        return NULL; // Program stop in here.
    }
    return _objGetWritableDatabase;
}

#ifdef __cplusplus
    }
#endif

SQLiteOpenHelper.class

public synchronized SQLiteDatabase getWritableDatabase(String password) {
    return this.getWritableDatabase(password == null?null:password.toCharArray());
}

Based on guidance from pskink , I could solve the above problem by changing the code as below:

JNIEXPORT jobject JNICALL
Java_com_company_pos_DbHelper_getWritableSqlite(JNIEnv *e, jobject obj) {
    JNIEnv *env = getEnv();
    if (env == NULL || obj == NULL) {
        return NULL;
    }

    // Error in here when calling getWritableDatabase from net/sqlcipher/database/SQLiteOpenHelper
    _objGetWritableDatabase = env->CallObjectMethod(_clsSQLiteOpenHelper, _midGetWritableDatabase, env->NewStringUTF("MyPassword"));  
    if (_objGetWritableDatabase == NULL || tryCatch()) {
        return NULL; // Program stop in here.
    }
    return _objGetWritableDatabase;
}

To

JNIEXPORT jobject JNICALL
Java_com_company_pos_DbHelper_getWritableSqlite(JNIEnv *e, jobject obj) {
    JNIEnv *env = getEnv();
    if (env == NULL || obj == NULL) {
        return NULL;
    }

    _objGetWritableDatabase = env->CallObjectMethod(obj, _midGetWritableDatabase, env->NewStringUTF("MyPassword"));  
    if (_objGetWritableDatabase == NULL || tryCatch()) {
        return NULL;
    }
    return _objGetWritableDatabase;
}

UNSECURED PASSWORD

You can change the MyPassword with hex string values (base 16). But store password in the form of string is not secured, we can view the pasword by opening the .so file with notepad++

static char *MyPassword = (char *) "93CEFC75923EA0370B6B04CECA4E7A99"

SECURED PASSWORD

Store the encrypted password in the form of a byte array is more difficult to read. Decrypt the password when reading. Examples like this:

const size_t BIT_LENGTH = 16;
static unsigned char bit_pwd[] = {0x13, 0x4E, 0x83, 0xF4, 0xEC, 0xBC, 0xDD, 0xB4, 0x77, 0xEF, 0x7F, 0x4B, 0xB0, 0xC8, 0x03, 0x1E};
static unsigned char chr_pwd[BIT_LENGTH * 2];

char *getPwd() {
    unsigned char c1 = 0x80;
    unsigned char c2 = c1;
    unsigned char *p = chr_pwd;

    memset(chr_pwd, '\0', BIT_LENGTH * 2);
    for (jint i = 0; i < BIT_LENGTH; i++) {
        if (i % 2 != 0) {
            bit_pwd[i] ^= c1++;
        }
        else {
            bit_pwd[i] ^= c2--;
        }
        p += sprintf((char *) p, "%02X", bit_pwd[i]);
    }
    chr_pwd[BIT_LENGTH * 2] = '\0';
    return (char *) chr_pwd;
}

// You will get real password ("93CEFC75923EA0370B6B04CECA4E7A99") from getPwd method.
_objGetWritableDatabase = env->CallObjectMethod(obj, _midGetWritableDatabase, env->NewStringUTF(getPwd()));  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM