Fail to open database after encryption in Android


#1

Hi, I’m a bit worried this may be duplicate issue, but not any answer helped to resolve my issue, so I would like to share my code to get some idea what’s going wrong.

private static synchronized void createSession(Context context) {
    try {
        SQLiteDatabase.loadLibs(context);
        encrypt(context, DBOpenHelper.DB_NAME, "password");
    } catch (IOException e) {
        e.printStackTrace();
    }
    DBOpenHelper helper = new DBOpenHelper(context);
    db = helper.getEncryptedWritableDb("password");
}

private static void encrypt(Context context, String dbName, String passphrase) throws IOException {
    File originalFile = context.getDatabasePath(dbName);
    File encryptedFile = context.getDatabasePath(ENCRYPTED_DB_NAME);

    if (originalFile.exists() && !encryptedFile.exists()) {
        encryptedFile.mkdir();
        SQLiteDatabase.openOrCreateDatabase(encryptedFile, passphrase, null);

        SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE);

        db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
                encryptedFile.getAbsolutePath(), passphrase));

        db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
        db.rawExecSQL("DETACH DATABASE encrypted;");

        int version = db.getVersion();

        db.close();

        db = SQLiteDatabase.openDatabase(encryptedFile.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READWRITE);

        db.setVersion(version);
        db.close();

        originalFile.delete();
        encryptedFile.renameTo(originalFile);
    }
}

This is my code to encrypt original local unencrypted databases whenever the application is updated. However, I’m getting error when I try to open the encrypted database even though I’m using the same passphrase(in this case, “password”). Following is the error I’m receiving.

03-22 11:37:17.599 9080-9080/com.example E/SQLiteOpenHelper: Couldn't open %my-db-name% for writing (will try read-only):
                                                               net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
                                                                   at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
                                                                   at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
                                                                   at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
                                                                   at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:89)
                                                                   at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
                                                                   at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
                                                                   at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
                                                                   at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1785)
                                                                   at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2486)
                                                                   at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2415)
                                                                   at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1149)
                                                                   at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1212)
                                                                   at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:162)
                                                                   at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:227)
                                                                   at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:214)
                                                                   at org.greenrobot.greendao.database.DatabaseOpenHelper.getEncryptedWritableDb(DatabaseOpenHelper.java:134)

There is no problem when I pass empty string when I open my database: it just opens unencrypted database.
How can I open the encrypted database with my passphrase?


#2

Hi @min0623

Try removing this line from your example:

SQLiteDatabase.openOrCreateDatabase(encryptedFile, passphrase, null);

The ATTACH statement will create the file if it doesn’t already exist. We have an example of importing a plain-text database, encrypting it here.


#3

Thanks @developernotes

Removing the line throws another SQLite exception on the first rawExecSQL.

Caused by: net.sqlcipher.database.SQLiteException: unable to open database: /data/user/0/my.package/databases/db-encrypted
                                                                    at net.sqlcipher.database.SQLiteDatabase.native_rawExecSQL(Native Method)
                                                                    at net.sqlcipher.database.SQLiteDatabase.rawExecSQL(SQLiteDatabase.java:2281) 

This is another error that occured.

E/Database: close() was never explicitly called on database '/data/user/0/my.package/databases/remember-db' 
                                                       net.sqlcipher.database.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
                                                           at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:2402)
                                                           at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1148)
                                                           at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1116)
                                                           at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1065)
                                                           at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1019)

This was why I inserted that line in my code. Any idea what is happening?


#4

Hi @min0623

What are your results of running the SQLCipher for Android test suite?