'file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master;

I have an app that has been in production for years. It ships with the standard Sqlite DB. The lastest version of the app has SqlCipher 3.5.6 library integrated.

There are some extra tables in the new DB, so i have created them in the onUpgrade method.

I have called the following method in my Application Object.

SQLiteDatabase.loadLibs(this);

The DB_VERSION in the old apk is 56 and i have set it to 57 in the new apk, so onUpgrade should be called.

I call the encrypt method from inside onUpgrade.

@Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

            Log.e(TAG, "++++++++++++++++++++++++++SQLiteOpenHelper onUpgrade old version = " + oldVersion + " new version = " + newVersion);

if(oldVersion == 56){

                String sqlToCreateWeeklySummary = String
                        .format("create table %s ( %s INTEGER primary key, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT)",
                                TABLEWEEKLYSUMMARY, C_ID_WEEKLY_SUMMARY, C_WEEKLY_SUMMARY_STARTDATE,
                                C_WEEKLY_SUMMARY_PLANNEDCALLS, C_WEEKLY_SUMMARY_NCR, C_WEEKLY_SUMMARY_QA, C_WEEKLY_SUMMARY_PLANNEDHOURS,
                                C_WEEKLY_SUMMARY_ACTUALCALLS, C_WEEKLY_SUMMARY_ACTUALHOURS);

                db.execSQL(sqlToCreateWeeklySummary);
                Log.e(TAG, "onUpgrade " + sqlToCreateWeeklySummary);


more table upgrades................

                NfcScannerApplication.setJustUpgradedDBTrue();
                NfcScannerApplication.setDownloadingCompOptsAfterUpgradeTrue();




                try {
                    encrypt(context, "carefreemobiledb.db", "");
                }catch(Exception e){}

            }



        }//end of onUpgrade

.

public void encrypt(Context ctxt, String dbName, String passphrase) throws IOException {

        Log.e(TAG, "inside encrypt");

        File originalFile=ctxt.getDatabasePath(dbName);

        if (originalFile.exists()) {
            Log.e(TAG, "originalFile exists1");
            File newFile= File.createTempFile("sqlcipherutils", "tmp", ctxt.getCacheDir());
            Log.e(TAG, "created newFile2");

            SQLiteDatabase db= SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
                    "", null, SQLiteDatabase.OPEN_READWRITE);
            Log.e(TAG, "opened DB using originalFile3");

            db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
                    newFile.getAbsolutePath(), passphrase));
            Log.e(TAG, "Attached DB4");

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

            Log.e(TAG, "export5");

            db.rawExecSQL("DETACH DATABASE encrypted;");
            Log.e(TAG, "detached DB6");

            int version=db.getVersion();

            db.close();
            Log.e(TAG, "closed DB7");

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

            Log.e(TAG, "opened DB with newFile8");

            db.setVersion(version);
            Log.e(TAG, "set version for db using newFile9");
            db.close();
            Log.e(TAG, "closed db10");

            originalFile.delete();
            Log.e(TAG, "deleted orignial file11");
            newFile.renameTo(originalFile);
            Log.e(TAG, "renamed newFile to orginalFile12");
        }
    }

When i load the new apk onto a device that has the old app installed, i get the following error.

file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master;
                                           net.sqlcipher.database.SQLiteException: file is encrypted or 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(Unknown Source)
                                               at net.sqlcipher.database.SQLiteCompiledSql.<init>(Unknown Source)
                                               at net.sqlcipher.database.SQLiteProgram.<init>(Unknown Source)
                                               at net.sqlcipher.database.SQLiteQuery.<init>(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDirectCursorDriver.query(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.rawQuery(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.keyDatabase(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.openDatabase(Unknown Source)
                                               at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(Unknown Source)
                                               at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(Unknown Source)
                                               at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(Unknown Source)
                                               at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(Unknown Source)
                                               at com.carefreegroup.rr3.g.c(Unknown Source)
                                               at com.carefreegroup.rr3.EntryActivity.onCreate(Unknown Source)
                                               at android.app.Activity.performCreate(Activity.java:6912)
                                               at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
                                               at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2900)
                                               at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3008)
                                               at android.app.ActivityThread.-wrap14(ActivityThread.java)
                                               at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
                                               at android.os.Handler.dispatchMessage(Handler.java:102)
                                               at android.os.Looper.loop(Looper.java:154)
                                               at android.app.ActivityThread.main(ActivityThread.java:6688)
                                               at java.lang.reflect.Method.invoke(Native Method)
                                               at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
                                               at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)
04-26 10:40:53.332 9417-9417/? E/SQLiteOpenHelper: Couldn't open carefreemobiledb.db for writing (will try read-only):

What i have tried:

Other posts on SO say that the password on the encrypt method needs to be an empty String so that SqlCipher can open an unencrypted DB.

I have set the password to "" on the encrypt method, but the logs says that the DB couldn't be opened.

Can anyone tell me why the DB is not being opened? It is not getting as far as the onUpgrade method.

NB. it may be worth noting that when i clear the data on the app, it works fine.

[Update 1]

I have moved the encrypt method into the Application class. In onCreate of the Application class i make a call to:

SQLiteDatabase.loadLibs(this);
        Log.e(TAG, "just called SQLiteDatabase.loadLibs(this)");


        try {
            encrypt(mContext, "carefreemobiledb.db", "");
        }catch(Exception e){}

.

There is still an empty String parameter in the encrypt method for the passphrase but it does nothing as i have commented it out in the actual encrypt method below.

public static void encrypt(Context ctxt, String dbName, String passphrase) throws IOException {

        File originalFile=ctxt.getDatabasePath(dbName);

        if (originalFile.exists()) {
            Log.e(TAG, "originalFile exists1");
            File newFile= File.createTempFile("sqlcipherutils", "tmp", ctxt.getCacheDir());
            Log.e(TAG, "created newFile2");

            SQLiteDatabase db= SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
                            "", null, SQLiteDatabase.OPEN_READWRITE);
            Log.e(TAG, "opened DB using originalFile3");

//          db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
//                  newFile.getAbsolutePath(), passphrase));
            db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
                    newFile.getAbsolutePath()));
            Log.e(TAG, "Attached DB4");

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

            Log.e(TAG, "export5");

            db.rawExecSQL("DETACH DATABASE encrypted;");
            Log.e(TAG, "detached DB6");

            int version=db.getVersion();

            db.close();
            Log.e(TAG, "closed DB7");

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

            Log.e(TAG, "opened DB with newFile8");

            db.setVersion(version);
            Log.e(TAG, "set version for db using newFile9");
            db.close();
            Log.e(TAG, "closed db10");

            originalFile.delete();
            Log.e(TAG, "deleted orignial file11");
            newFile.renameTo(originalFile);
            Log.e(TAG, "renamed newFile to orginalFile12");
        }
    }

.

I now get the following error:

04-26 13:56:02.793 15460-15460/? E/NfcScannerApplication: just called SQLiteDatabase.loadLibs(this)
04-26 13:56:02.793 15460-15460/? E/NfcScannerApplication: originalFile exists1
04-26 13:56:02.794 15460-15460/? E/NfcScannerApplication: created newFile2
04-26 13:56:02.798 15460-15460/? E/NfcScannerApplication: opened DB using originalFile3
04-26 13:56:02.806 3247-3247/? E/audit: type=1320 audit(1493211362.789:23690): 
04-26 13:56:02.817 3247-3247/? E/audit: type=1320 audit(1493211362.799:23691): 
04-26 13:56:02.828 3247-3247/? E/audit: type=1320 audit(1493211362.809:23692): 
04-26 13:56:02.886 15460-15469/? E/Database: close() was never explicitly called on database '/data/user/0/com.carefreegroup.rr3/databases/carefreemobiledb.db' 
                                             net.sqlcipher.database.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
                                                 at net.sqlcipher.database.SQLiteDatabase.<init>(Unknown Source)
                                                 at net.sqlcipher.database.SQLiteDatabase.openDatabase(Unknown Source)
                                                 at net.sqlcipher.database.SQLiteDatabase.openDatabase(Unknown Source)
                                                 at net.sqlcipher.database.SQLiteDatabase.openDatabase(Unknown Source)
                                                 at net.sqlcipher.database.SQLiteDatabase.openDatabase(Unknown Source)
                                                 at com.carefreegroup.rr3.NfcScannerApplication.a(Unknown Source)
                                                 at com.carefreegroup.rr3.NfcScannerApplication.onCreate(Unknown Source)
                                                 at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1032)
                                                 at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5881)
                                                 at android.app.ActivityThread.-wrap3(ActivityThread.java)
                                                 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1718)
                                                 at android.os.Handler.dispatchMessage(Handler.java:102)
                                                 at android.os.Looper.loop(Looper.java:154)
                                                 at android.app.ActivityThread.main(ActivityThread.java:6688)
                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

.

It gets as far as the following and crashes:

Log.e(TAG, "opened DB using originalFile3");

.

[Update 2]

I'm not sure if i'm closing the database correctly. LoginValidate is my DB class which has a method exposed that returns the DB object.

SQLiteDatabase.loadLibs(this);
        Log.e(TAG, "just called SQLiteDatabase.loadLibs(this)");

        loginValidate.getDB().close();

        try {
            encrypt(mContext, "carefreemobiledb.db", "");
        }catch(Exception e){}

.

Also i'm not sure about the passphrase in the encrypt method. You say not to use a passphrase, but does that mean and empty String or not to pass in the passphrase at all. i'll give the following example. The first is the orginal, the other 2 are what i have tried.

Log.e(TAG, "opened DB using originalFile3");

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




Log.e(TAG, "opened DB using originalFile3");

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



Log.e(TAG, "opened DB using originalFile3");

            db.rawExecSQL(String.format("ATTACH DATABASE '%s';",
                    newFile.getAbsolutePath()));


Solution 1:[1]

Can anyone tell me why the DB is not being opened?

Because you are trying to open it with the passphrase, and that is not working, because the database is not encrypted. SQLCipher for Android does not magically decide to try to open the database a second time, this time without the passphrase, and then call onUpgrade().

Besides, how are you even getting to this state? You should be collecting the passphrase from the user, and the user is going to look at you funny if you all of a sudden start asking for a passphrase that they have never set. Instead, you need to have a specific UX for "hey, let's switch to an encrypted database!", where the user sets the passphrase and you go through encrypt().

Solution 2:[2]

You might get this error:

file is not a database: , while compiling: select count() from sqlite_master; net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count() from sqlite_master;

if your db file was only partially copied to the databases folder.

Go to Device Explorer, go to data/data/your app folder/databases, Make sure you have a valid copy of your db file in that folder.

Solution 3:[3]

It may be a version mismatch between the tool used to encrypt the DB and the Lib used to open it.

If you follow the instructions on the official website, it should work:

https://www.zetetic.net/sqlcipher/sqlcipher-for-android/

It worked for me, as I started using a newer version (4.4.3) of the SQLite client to encrypt the database, but was still using the older libs in Android.

Adding the following to the build.gradle(:app) fixed it:

implementation 'net.zetetic:android-database-sqlcipher:4.5.1@aar'
implementation "androidx.sqlite:sqlite:2.2.0"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CommonsWare
Solution 2 live-love
Solution 3 Victor Lee