My app is currently using room database. I'm tying to migrate to use Sqlcipher data base. I have fallbackToDestructiveMigration()
enabled but still throwing the following error
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: 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:91)
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:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2673)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2603)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
at androidx.room.util.DBUtil.query(DBUtil.java:83)
at com.screenlocker.secure.room.MyDao_Impl$29.call(MyDao_Impl.java:1249)
at com.screenlocker.secure.room.MyDao_Impl$29.call(MyDao_Impl.java:1246)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Is there any way to destroy all my database and move to Sqlcipher? i have also tried database.delete("table_name",null,null)
command to manually deleting tables n migration but it is still not working. My code fro database creation is following.
DatabaseSecretProvider provider = new DatabaseSecretProvider(context);
byte[] passphrase = provider.getOrCreateDatabaseSecret().asBytes();
SupportFactory factory = new SupportFactory(passphrase);
instance = Room.databaseBuilder(context, MyAppDatabase.class, AppConstants.DATABASE_NAME)
.openHelperFactory(factory)
.fallbackToDestructiveMigration()
.build();
I'm using following version of Sqlcipher
implementation 'net.zetetic:android-database-sqlcipher:4.3.0'
implementation "androidx.sqlite:sqlite:2.1.0"
You can use AutoMigrationSpec to give Room the additional information that it needs to correctly generate migration paths. Define a static class that implements AutoMigrationSpec in your RoomDatabase class and annotate it with one or more of the following: @DeleteTable. @RenameTable.
For use with dotConnect for SQLite, it must be renamed to sqlite3. dll. To connect to SQLCipher encrypted database, you should set the Encryption connection string parameter to SQLCipher and specify the Password and Encryption License Key connection string parameters.
SQLCipher is an open source library that provides transparent, secure 256-bit AES encryption of SQLite database files.
You can encrypt an unencrypted database with the sqlcipher_export
convenience method from sqlcipher. So you don't have to do use fallbackToDestructiveMigration
or spend time writing your custom migration tool.
From the developer's website:
To use sqlcipher_export() to encrypt an existing database, first open up the standard SQLite database, but don’t provide a key. Next, ATTACH a new encrypted database, and then call the sqlcipher_export() function in a SELECT statement, passing in the name of the attached database you want to write the main database schema and data to.
$ ./sqlcipher plaintext.db
sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'newkey';
sqlite> SELECT sqlcipher_export('encrypted');
sqlite> DETACH DATABASE encrypted;
Finally, securely delete the existing plaintext database, and then open up the new encrypted database as usual using sqlite3_key or PRAGMA key.
Source: https://discuss.zetetic.net/t/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher-and-avoid-file-is-encrypted-or-is-not-a-database-errors/868
@commonsguy also has an example of how to do this in Android:
/**
* Replaces this database with a version encrypted with the supplied
* passphrase, deleting the original. Do not call this while the database
* is open, which includes during any Room migrations.
*
* @param ctxt a Context
* @param originalFile a File pointing to the database
* @param passphrase the passphrase from the user
* @throws IOException
*/
public static void encrypt(Context ctxt, File originalFile, byte[] passphrase)
throws IOException {
SQLiteDatabase.loadLibs(ctxt);
if (originalFile.exists()) {
File newFile=File.createTempFile("sqlcipherutils", "tmp",
ctxt.getCacheDir());
SQLiteDatabase db=
SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
"", null, SQLiteDatabase.OPEN_READWRITE);
int version=db.getVersion();
db.close();
db=SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase,
null, SQLiteDatabase.OPEN_READWRITE, null, null);
final SQLiteStatement st=db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''");
st.bindString(1, originalFile.getAbsolutePath());
st.execute();
db.rawExecSQL("SELECT sqlcipher_export('main', 'plaintext')");
db.rawExecSQL("DETACH DATABASE plaintext");
db.setVersion(version);
st.close();
db.close();
originalFile.delete();
newFile.renameTo(originalFile);
}
else {
throw new FileNotFoundException(originalFile.getAbsolutePath()+ " not found");
}
}
Source: https://github.com/commonsguy/cwac-saferoom/blob/v1.2.1/saferoom/src/main/java/com/commonsware/cwac/saferoom/SQLCipherUtils.java#L175-L224
You can pass context.getDatabasePath(DATABASE_NAME)
as originalFile
parameter.
Also, see this comment from commonsguy which explains how you can use this in combination with the getDatabaseState
function to migrate your data from an existing plain-text database to a sqlcipher encrypted database.
This worked for me but I feel like its not the best answer:
val factory: SupportFactory = SupportFactory(masterKeyAlias.toByteArray())
private fun buildDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"MyDatabaseNew.db" // <<--- change the name of this database file
).openHelperFactory(factory)
.build()
This is a brand new database and the data needs to be populated brand new. Maybe there is a way to migrate it in a migration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With