简体   繁体   中英

SQLCipher can't open database after apprestart

What I'm trying to do


In the app I'm creating we're saving secret user data that needs to be encrypted. For that we'd like to use SQLCipher. We've implemented as in the documentation.

Now I'm facing following problem. If I create the database for the first time I can read and write the db. If I restart the app I'm unable to read the database again and get tons of errors. What am I doing wrong?

Here's the Code of our SQLiteOpenHeler:

public Database (Context ctx){
        super(ctx, DATABASE_NAME, null, DATABASE_VERSION);

        try{
            mKeySaver = new KeySaver(ctx);
            File databaseFile = ctx.getDatabasePath(Database.DATABASE_NAME);

            if(databaseFile.exists()){
                SQLiteDatabaseHook hook = new SQLiteDatabaseHook(){
                    public void preKey(SQLiteDatabase database){
                        database.rawExecSQL("PRAGMA kdf_iter = 5000");
                    }
                    public void postKey(SQLiteDatabase database){}
                };
                mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, mKeySaver.getKey(), null, hook);

            } else {
                databaseFile.mkdir();
                mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, mKeySaver.getKey(), null);
                mDb.setLocale(Locale.GERMAN);
            }

        } catch (Exception e){
            Log.d("Database", "Exception " + e);
        }
    }


    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        try{
            sqLiteDatabase.execSQL(CREATE_TABLE_ACCOUNT);
            sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_DETAILS);
            sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_OVERVIEW);
            sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_UPLOAD);
            sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_USER);
            sqLiteDatabase.execSQL(CREATE_TABLE_ACCOUNT);
        }catch (Exception e){
            Log.e("Database", "Exception " + e);
        }

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion >= newVersion) {
            return;
        }

        // Falls ein DB-Update stattfinden sollte Code hier

    }

And that's how we instantiate it:

SQLiteDatabase.loadLibs(mContext.getApplicationContext());
mKeySaver = new KeySaver(mContext);
mDb = new Database(mContext.getApplicationContext());
db_password = mKeySaver.getKey();
mDatabase = mDb.getWritableDatabase(db_password);

And the Logcatdump:

04-23 09:00:44.723    2405-2405/ivo E/Database﹕ CREATE TABLE android_metadata failed
04-23 09:00:44.726    2405-2405/ivo E/Database﹕ Failed to setLocale() when constructing, closing the database
    net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
            at net.sqlcipher.database.SQLiteDatabase.native_setLocale(Native Method)
            at net.sqlcipher.database.SQLiteDatabase.setLocale(SQLiteDatabase.java:2092)
            at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1958)
            at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:875)
            at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:907)
            at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
            at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:197)
            at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
            at ivo.database.adapter.AccountAdapter.checkAccountPassword(AccountAdapter.java:79)
            at ivo.activity.LoginActivity.attemptLogin(LoginActivity.java:168)
            at ivo.activity.LoginActivity$2.onClick(LoginActivity.java:88)
            at android.view.View.performClick(View.java:4756)
            at android.view.View$PerformClick.run(View.java:19748)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
04-23 09:00:44.730    2405-2405/swisscom.ivocore E/SQLiteOpenHelper﹕ Couldn't open ivo.db for writing (will try read-only):
    net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
            at net.sqlcipher.database.SQLiteDatabase.native_setLocale(Native Method)
            at net.sqlcipher.database.SQLiteDatabase.setLocale(SQLiteDatabase.java:2092)
            at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1958)
            at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:875)
            at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:907)
            at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
            at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:197)
            at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
            at ivo.database.adapter.AccountAdapter.checkAccountPassword(AccountAdapter.java:79)
            at ivo.activity.LoginActivity.attemptLogin(LoginActivity.java:168)
            at ivo.activity.LoginActivity$2.onClick(LoginActivity.java:88)
            at android.view.View.performClick(View.java:4756)
            at android.view.View$PerformClick.run(View.java:19748)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)

你可能需要表ANDROID_METADATA与(键,值)=('locale',你的LOCALE)

It look like you are opening the database again when your app restart.I had the same error but I solve it by creating a helper class which cache the previously opened database and only open it if it is really needed like below.

public class Helper {

    private static net.sqlcipher.database.SQLiteDatabase database;

    public static net.sqlcipher.database.SQLiteDatabase openDatabase(Context context, String databaseName, String password) {
        if (database == null) {
            database = net.sqlcipher.database.SQLiteDatabase .openOrCreateDatabase(context.getDatabasePath(context.getResources().getString(database)), password, null);
         }

        return database;
    }
}

So when your app restart try to called openDatabase() method again.

This line looks weird to me:

databaseFile.mkdir();

mkdir() creates a folder, hence you are passing a folder instead of a file to openOrCreateDatabase() .

Also I think you shouldn't call openOrCreateDatabase() in the SQLiteOpenHelper's constructor. It takes care of creating the database file automatically. Then you can get the SQLiteDatabase object by calling for example SQLiteOpenHelper.getWritableDatabase(password) .

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