SQLCipher 3.3.1-2 Could not open database


#1

Hello,

After searching for all the forum and documentation, I am not able to make the new version of SQLCipher work in Android.

I was using successfully a previous version of SQLCipher and I have migrated to 3.3.1-2 because of the issues with Android 6.0. I have erased all the previous files in my project related to SQLcipher, I mean, *.so, ICUDTL, *.jar and then I added in Gradle the compile line:
compile ‘net.zetetic:android-database-sqlcipher:3.3.1-2@aar’

But when I execute my app I get:

11-25 17:39:40.485 7504-7552/? I/Database: sqlite returned: error code = 14, msg = os_unix.c:34892: (2) open(/data/data/com.mydomain/databases/my_database_01) - 
11-25 17:39:40.485 7504-7552/? E/Database: sqlite3_open_v2("/data/data/com.mydomain/databases/my_database_01", &handle, 1, NULL) failed
11-25 17:39:40.495 7504-7552/? E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
                                                 Process: com.mydomain, PID: 7504
                                                 java.lang.RuntimeException: An error occured while executing doInBackground()
                                                     at android.os.AsyncTask$3.done(AsyncTask.java:304)
                                                     at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
                                                     at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
                                                     at java.util.concurrent.FutureTask.run(FutureTask.java:242)
                                                     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
                                                     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                                                     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                                                     at java.lang.Thread.run(Thread.java:818)
                                                  Caused by: net.sqlcipher.database.SQLiteException: error code 14: Could not open database
                                                     at net.sqlcipher.database.SQLiteDatabase.dbopen(Native Method)
                                                     at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2322)
                                                     at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1087)
                                                     at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1028)
                                                     at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:956)
                                                     at com.meapp.data.DataBaseHelper.openDataBase(DataBaseHelper.java:189)
                                                     at com.meapp.activities.Main_activity.checkUpdate_DB_3(Main_activity.java:307)
                                                     at com.meapp.activities.Main_activity.access$1100(Main_activity.java:61)
                                                     at com.meapp.activities.Main_activity$ChecksStartUP.doInBackground(Main_activity.java:579)
                                                     at com.meapp.activities.Main_activity$ChecksStartUP.doInBackground(Main_activity.java:549)
                                                     at android.os.AsyncTask$2.call(AsyncTask.java:292)
                                                     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                                                     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
                                                     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
                                                     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
                                                     at java.lang.Thread.run(Thread.java:818) 
11-25 17:39:40.502 868-1626/? D/ActivityManager: New dropbox entry: com.mydomain, data_app_crash, 47367f72-c553-4e8c-a95a-43aa8829a8e2
11-25 17:39:40.503 868-1626/? W/ActivityManager:   Force finishing activity 1 com.mydomain/com.meapp.activities.Main_activity
11-25 17:39:41.617 1617-1617/? W/PackageManager: Failure retrieving resources for com.mydomain: Resource ID #0x0
11-25 17:44:40.587 868-2080/? I/ActivityManager: Process com.mydomain (pid 7504) has died

I found that when migrating from a previous version of the library some modificacions must be performed like @developernotes says here File is encrypted or is not a database after upgrade SQLCipher from v.2.2.2.0 to v.3.1.0

So to avoid openning a previous version, I uninstalled my application and make sure that NO ddbb existed when trying to open it, that it was the standard behaviour that worked well previously.

Could you give me some hint about what could be happening?
Thank you very much!!


#2

Could be that the openDataba from the old library didn’t throw an SQLiteException but a IOException?


#3

Hi @Chezlui

So is this just a new database being created from scratch with SQLCipher for Android 3.3.1-2 using the AAR reference? If so, can you post the code that is executing that causes the exception to occur?


#4

No @developernotes, I was wrong. I had two errors concatenated:

  • First, I whas throwing a SQLiteException that I was just simply not handling it. It seems to be a new exception, or for any reason I didn’t have that problem before.

  • Second, I was downloading an old format ddbb. Duhhh…

I’m sorry for this. To compensate, I bring a code based in your CipherMigrateTest that I created to migrate the DDBB when I catch the error, :stuck_out_tongue:

package com.meapp.data.tools;

import java.io.File;

import android.util.Log;
import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;


public class CipherMigrate {

	public boolean execute(SQLiteDatabase database, File olderFormatDatabase) {
		if((database != null) && (database.isOpen()) ){
			database.close();
		}
		final boolean[] status = {false};
		try {
			SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
				public void preKey(SQLiteDatabase database) {}
				public void postKey(SQLiteDatabase database) {
					String value = QueryHelper.singleValueFromQuery(database, "PRAGMA cipher_migrate");
					status[0] = Integer.valueOf(value) == 0;
				}
			};
			database = SQLiteDatabase.openOrCreateDatabase(olderFormatDatabase,
					ddBBKey, null, hook);
			if(database != null){
				database.close();
			}
		} catch (Exception e) {
			Log.i("DDBB Migration error", "error", e);
		}
		return status[0];
	}

	public String getName() {
		return "Cipher Migrate Test";
	}

	public static class QueryHelper {

		public static String singleValueFromQuery(SQLiteDatabase database, String query){
			Cursor cursor = database.rawQuery(query, new String[]{});
			String value = "";
			if(cursor != null){
				cursor.moveToFirst();
				value = cursor.getString(0);
				cursor.close();
			}
			return value;
		}

		public static int singleIntegerValueFromQuery(SQLiteDatabase database, String query){
			Cursor cursor = database.rawQuery(query, new String[]{});
			int value = 0;
			if(cursor != null){
				cursor.moveToFirst();
				value = cursor.getInt(0);
				cursor.close();
			}
			return value;
		}
	}
} 

And in DataBaseHelper.java, migrate the DDBB if it throws an error when opening it:

SQLiteDatabase meappDB;

public void openDataBase() throws SQLException {
	String myPath = IM_PATH + IMAG_DDBB;
	try {
		meappDB = SQLiteDatabase.openDatabase(myPath, configuracion, null, SQLiteDatabase.OPEN_READONLY);

	} catch (SQLiteException sql) {
		CipherMigrate cipherMigrate = new CipherMigrate();
		cipherMigrate.execute(meappDB, new File(myPath));
		meappDB = SQLiteDatabase.openDatabase(myPath, configuracion, null, SQLiteDatabase.OPEN_READONLY);
	} catch (Exception e) {
		e.printStackTrace();
	}

}

#5

Hello @Chezlui

You should also keep track of application migration from the 2.x format to 3.x externally from a SQLCIpher database. This will allow you to prevent the application from attempting to migrate a database when an invalid password is provided.


#6

@developernotes what do you mean with “keep track of application migration from the 2.x format to 3.x externally from a SQLCIpher database”?

Thank you very much for your hints!


#7

Hello @Chezlui

You should track, externally to the database, that you have performed a migration to the database file so that you are not performing a PRAGMA cipher_migrate; each time you open your database.