File not a database exception when exporting from SQLite DB to SQLCipher


#1
public static void migrateToEncrption(Context context, int i) throws IOException{
    SQLiteDatabase.loadLibs(context);
    File sqliteDbFile = context.getDatabasePath(SQLDataStore.DATABASE_NAME);
    File newFile = context.getDatabasePath(DATABASE_NAME_  + String.valueOf(i));
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(sqliteDbFile.getAbsolutePath(), "", null,         new SQLiteDatabaseHook() {
        @Override
        public void preKey(SQLiteDatabase database) {
    
        }

        @Override
        public void postKey(SQLiteDatabase db) {
            db.execSQL("PRAGMA cipher_default_page_size = 4096");
        }
    });
    db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s'", newFile.getAbsolutePath(), password));
//    db.execSQL("PRAGMA encrypted.cipher_page_size = 4096");
    db.execSQL("PRAGMA encrypted.auto_vacuum = 1");
    db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
    db.rawExecSQL("DETACH DATABASE encrypted;");
    int version = db.getVersion();
    db.close();
    db = SQLiteDatabase.openOrCreateDatabase(newFile.getAbsolutePath(), password, null);
    db.setVersion(version);
    db.close();
    //noinspection ResultOfMethodCallIgnored
    sqliteDbFile.delete();
}

public class DataStore extends SQLiteOpenHelper {
    public DataStore(Context context, int i) {
        super(context.getApplicationContext(), DATABASE_NAME_  + String.valueOf(i) ,
                    null  /* cursor factory */, DATABASE_VERSION);
        getWritableDatabase(password);
}

I’m using the above code to convert a SQLite DB to SQLCipher. The issue here is that whenever I try to set cipher_page_size for the encrypted DB, I’m getting the following error.

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.(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.(SQLiteProgram.java:89)
at net.sqlcipher.database.SQLiteQuery.(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.getWritableDatabase(SQLiteOpenHelper.java:129)
at <package_name>.Datastore.<init>(Line_no_of_getWritableDatabase)

Note that this doesn’t happen if I don’t set the cipher_page_size either by the commented method or the one in postKey method.


#2

I’m using android-database-sqlcipher:3.5.9


#3

Hi @chandraprakash

Setting cipher_default_page_size to 4096 in the postKey event of your database hook is setting the page size of the encrypted export to 4096, which will require you to set that value every time you open the connection. Unless you want a page size of 4k, you can remove that from both the hook and the subsequent PRAGMA encrypted.cipher_page_size as well.