【问题标题】:Calling getWritableDatabase (SQLCipher java method) from JNI从 JNI 调用 getWritableDatabase(SQLCipher java 方法)
【发布时间】:2017-03-20 09:40:21
【问题描述】:

我有一个 java 代码来打开 SqlCipher 数据库。我想从 JNI 调用 getWritableDatabase 函数来隐藏密码。

Java_com_company_pos_DbHelper_getWritableSqlite 方法 (sqlauth.cpp) 中的 SQLiteOpenHelper 类调用 getWritableDatabas 方法时出错,我的代码有什么问题?

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());
}

【问题讨论】:

  • _clsSQLiteOpenHelperjclass,应该是jobject
  • @pskink,将类的类型更改为对象时出现消息错误,如下所示“错误:(73, 48)错误:无法初始化'jclass'类型的参数(又名'_jclass *' ) 的左值类型为 'jobject' (aka '_jobject *')"
  • 我不知道,基本上CallObjectMethod的第二个参数是对象实例而不是类实例
  • GetWritableDatabase 方法在 SQLiteOpenHelper.class 中。如何调用具有输出同步 SQLiteDatabase 的方法?我尝试使用 CallObjectMethod 但仍然出错。
  • Java_com_company_pos_DbHelper_getWritableSqlite(JNIEnv *e, jobject obj) { 调用CallObjectMethod 时应使用jobject obj 作为第二个参数

标签: java android c++ java-native-interface sqlcipher


【解决方案1】:

根据pskink的指导,我可以通过如下修改代码来解决上述问题:

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;
}

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;
}

不安全的密码

您可以使用十六进制字符串值(以 16 为基数)更改 MyPassword。 但是以字符串的形式存储密码是不安全的,我们可以查看 用notepad++打开.so文件的密码

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

安全密码

以字节数组的形式存储加密密码更难阅读。阅读时解密密码。像这样的例子:

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()));  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多