4

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)
safari
  • 7,565
  • 18
  • 56
  • 82
  • 1
    What is `KeySaver`? Have you validated that `mKeySaver.getKey()` is returning what you are expecting? – CommonsWare May 04 '15 at 23:12
  • checked [file is encrypted or is not a database (Exception net.sqlcipher.database.SQLiteException)](http://stackoverflow.com/questions/22062277/file-is-encrypted-or-is-not-a-database-exception-net-sqlcipher-database-sqlitee) ? – Maveňツ May 05 '15 at 08:05
  • @CommonsWare Yes I've checked that - it returns the value as expected. It's a simple helper class which creates a random key and stores it into the SharedPreference. – safari May 15 '15 at 10:11

3 Answers3

0

you might need the table ANDROID_METADATA with (key,value)=('locale',YOUR LOCALE)

Bondax
  • 3,143
  • 28
  • 35
0

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.

Ziem
  • 6,579
  • 8
  • 53
  • 86
Sun Maung Oo
  • 183
  • 2
  • 11
  • Thank you for this answer - I will try that out today. Where to you start this helper class? In your SplashActivity or in the SQLiteOpenHelper? – safari Apr 29 '15 at 06:35
  • @safari I start it in SplashActivity after checking if the database exist first. – Sun Maung Oo Apr 30 '15 at 07:00
0

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).

Miloš Černilovský
  • 3,846
  • 1
  • 28
  • 30