From the last 3 days i am trying to upgrade my database to a higher version of SQLCipher library (v3.1.0). I did every step and followed a few tutorials too. But keep on getting the error "File is encrypted or not a Database". Now am trying to move to unencrypted database ie. simple sqlite database.
Do we have a way to move to encrypted database to un-encrypted database? Thanks in advance.
This is the code i am working on:
public MyDBAdapter(Context context) {
this.context = context;
File dbFile = context.getDatabasePath(DATABASE_NAME);
String dbPath = context.getDatabasePath(DATABASE_NAME).toString();
if (dbFile.exists()) {
try {
SQLiteDatabase.loadLibs(context.getApplicationContext());//load SqlCipher libraries
SQLiteDatabase db = getExistDataBaseFile(dbPath, KEY_PASSPHRASE_ENCRYPTION, dbFile);
if (version == 1) {
MigrateDatabaseFrom1xFormatToCurrentFormat(
dbFile, KEY_PASSPHRASE_ENCRYPTION);
}
System.out.println("Old Database found and updated.");
} catch (Exception e) {
System.out.println("No Old Database found");
}
}
this.dbhelper = new MyDBHelper(this.context, DATABASE_NAME, null,
DATABASE_Version);
db = dbhelper.getWritableDatabase(KEY_PASSPHRASE_ENCRYPTION);
}
private SQLiteDatabase getExistDataBaseFile(String FULL_DB_Path, String password, File dbFile) {// this function to open an Exist database
SQLiteDatabase.loadLibs(context.getApplicationContext());
SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
public void preKey(SQLiteDatabase database) {
System.out.println("-----Inside preKey");
}
public void postKey(SQLiteDatabase database) {
System.out.println("-----Inside postKey");
database.rawExecSQL("PRAGMA cipher_migrate;");
}
};
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(
dbFile, "Test123", null, hook); // Exception
return database;
}
If you are upgrading your SQLCipher library to the latest version, currently at 3.1.0, and your previous version was 2.x (as you mentioned in the comments above), you will need to upgrade the database file format as well. One of the big changes in the 3.x release was an increase in key derivation length, from 4,000 to 64,000. If you are using all of the standard SQLCipher configurations, upgrading the database format is straight forward. We have included a new PRAGMA call cipher_migrate that will perform this operation for you. You can execute this within the postKey event of the SQLiteDatabaseHook which is to be provided in your call to SQLiteDatabase.openOrCreateDatabase. An example of this can be found in the SQLCipher for Android test suite here.
Related
I am using SQLCipher v3.5.7 and observed an unexpected behavior from SQLiteDatabase with incorrect password.
I encrypted the database with "key1".
Closed the database connection.
Then I tried to open my database with "key2", the SQLiteDatabase is not throwing an exception. Instead, it is updating the old password (key1) to new password (key2). I verified this by opening the .db file in SQLiteBrowser.
Can somebody help me why it is behaving this way?
private static SQLiteCipherDatabaseHelper createDBConnection(Context context, String databasePath, final String key) throws SQLiteDatabaseException {
if (dbInstance == null) {
dbInstance = new SQLiteCipherDatabaseHelper(context, databasePath);
String path = context.getDatabasePath(databasePath).getPath();
File dbPathFile = new File(path);
if (!dbPathFile.exists()) {
dbPathFile.getParentFile().mkdirs();
}
setDatabaseWithDBEncryption(key);
}
return dbInstance;
}
private static void setDatabaseWithDBEncryption(String encryptionKey) throws SQLiteDatabaseException {
loadSQLCipherLibs();
try {
sqliteDatabase = SQLiteDatabase.openOrCreateDatabase(new File(context.getDatabasePath(databasePath).getPath()), encryptionKey, null);
} catch (Exception e) {
SyncLogger.getSharedInstance().logFatal("SQLiteCipherDatabaseHelper", "Failed to open or create database. Please provide a valid encryption key");
throw new SQLiteDatabaseException(SyncErrorCodes.EC_DB_SQLCIPHER_FAILED_TO_OPEN_OR_CREATE_DATABASE, SyncErrorDomains.ED_OFFLINE_OBJECTS, SyncErrorMessages.EM_DB_SQLCIPHER_FAILED_TO_OPEN_OR_CREATE_DATABASE, e);
}
}
Have you upgrade your db version ??
private static final int DATABASE_VERSION = 2;//from 1 to 2
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) // constructor
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
//Changes in db mentioned here
}
}
Are you actually populating the database with tables and data after keying it? It seems most likely that for some reason you are recreating the database each time you run the test. Have you verified that the actual database is encrypted by pulling it off the device and examining the file? Perhaps you are recreating a new database each time you run the test, in which case the new key would just be used.
It's worth noting that this behavior is covered in the SQLCipher for Android Test project.
https://github.com/sqlcipher/sqlcipher-android-tests/blob/master/src/main/java/net/zetetic/tests/InvalidPasswordTest.java
If you suspect an issue you can try running the test suite on your device, or create a new test case to verify the behavior with your own code.
Whenever I update my database I get this error. But when I rerun the app as it is, the database gets updated.
android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)[
Code:
public DBAdapter(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
ctx = context;
db = getWritableDatabase();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != newVersion) {
ctx.deleteDatabase(DATABASE_NAME);
new DBAdapter(ctx);
} else {
super.onUpgrade(db, oldVersion, newVersion);
}
}
As one of the SO answers suggested, I have added this too:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
BTW: I am using SQLiteAssetHelper to create prebuilt database
This is not a solution to prevent this issue but a work around.
public DBAdapter(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
ctx = context;
try {
db = getWritableDatabase();
} catch (SQLiteReadOnlyDatabaseException e){
ctx.startActivity(new Intent(ctx, MainActivity.class));
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != newVersion) {
ctx.deleteDatabase(DATABASE_NAME);
new DBAdapter(ctx);
} else {
super.onUpgrade(db, oldVersion, newVersion);
}
}
First time when the adapter is initialized, a writable db is created. Then onUpgrade gets called. Here when the database is deleted, the adapter get reinitialized. But the connection of the db is not deleted and persists hence, the second time when db = getWritableDatabase(); is executed SQLiteReadOnlyDatabaseException occurs. The original activity that initialized DBAdapter is restarted. Now the Adapter is reinitialized and the onUpgrade method is not called and hence SQLiteReadOnlyDatabaseException does not occur.
All this process happens very fast and the user experience does not become bad in my case.
Note: new DBAdapter(ctx); does not seem to be necessary and deleteDatabase seems to recreate the adapter. But for caution, I have written this line of code.
I would love to get some information on the cause and solution for this error.
I had some similar issues with Android SQLite databases. I submitted a bug report on it long ago at https://code.google.com/p/android/issues/detail?id=174566. This report discusses my findings on the reasons in more detail. I am not sure if it is related to your issue or not, but it seems to share some characteristics.
To summarize here, my debugging indicated that Android opens the database file, calls onUpgrade(), and if you replace the database file during the onUpgrade() call, the Android side file handle points to the old file and thus causes the app to crash when you return from onUpgrade() and Android tries to access the old file.
Here is some code I used to get around the issue:
When the app starts, I did this in onCreate():
Thread t = new Thread(new Runnable() {
#Override
public void run() {
Context context = getApplicationContext();
DBReader.copyDB(MainActivity.this);
DBReader.initialize(context);
}
});
t.start();
This causes the update of the database file to happen in the background while the app is starting and user is occupied with awe of the awesome application. Because my file was rather big and it took a while to copy. Notice that I completely avoid doing anything in onUpgrade() here.
DBReader is my own class, for which the main code of interest is this:
SharedPreferences prefs = context.getSharedPreferences(Const.KEY_PREFERENCES, Context.MODE_PRIVATE);
//here we have stored the latest version of DB copied
String dbVersion = prefs.getString(Const.KEY_DB_VERSION, "0");
int dbv = Integer.parseInt(dbVersion);
if (checkIfInitialized(context) && dbv == DBHelper.DB_VERSION) {
return;
}
File target = context.getDatabasePath(DBHelper.DB_NAME);
String path = target.getAbsolutePath();
//Log.d("Awesome APP", "Copying database to " + path);
path = path.substring(0, path.lastIndexOf("/"));
File targetDir = new File(path);
targetDir.mkdirs();
//Copy the database from assets
InputStream mInput = context.getAssets().open(DBHelper.DB_NAME);
OutputStream mOutput = new FileOutputStream(target.getAbsolutePath());
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0) {
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
mOutput.close();
mInput.close();
SharedPreferences.Editor edit = prefs.edit();
edit.putString(Const.KEY_DB_VERSION, "" + DBHelper.DB_VERSION);
edit.apply();
and the code for checkIfInitialized():
public static synchronized boolean checkIfInitialized(Context context) {
File dbFile = context.getDatabasePath(DBHelper.DB_NAME);
return dbFile.exists();
}
So, to make the story short, I just avoided onUpgrade() alltogether and implemented my own custom upgrade functionality. This avoids the problem of the Android OS crashing on old and invalid filehandles caused by change of the database in onUpgrade().
Kind of odd, I though, for the onUpgrade() to cause the OS to crash your app if you actually end up upgrading your database file in a function intended to let you upgrade your database. And the Google comments on the bug report were made few years after so I no longer had the original crashing code around for easy proof of concept.
Your problem might be slightly different in that you are not copying the database file, but you still seem to be modifying it, so the root cause might be similar.
Due to last release of Robolectic to version 2.3, it's written that (https://github.com/robolectric/robolectric/releases):
Robolectric now uses a real implementation of SQLite instead of a collection of shadows and fakes. Tests can now be written to verify real database behavior.
I haven't found any "How to" documentation.
I'd like to know how should I implement test on e.g. Activity using SQLiteDatabase query. Where should I put .db file so a test uses it.
You will need to put the .db file under src/test/resources/ folder.
For example, sample.db
Then in your unit test setUp() call:
#Before
public void setUp() throws Exception {
String filePath = getClass().getResource("/sample.db").toURI().getPath();
SQLiteDatabase db = SQLiteDatabase.openDatabase(
(new File(filePath)).getAbsolutePath(),
null,
SQLiteDatabase.OPEN_READWRITE);
// perform any db operations you want here
}
Here is an example how to test database, oprations.
// just a wrapper for the content values map
Agenda agenda = new Agenda();
agenda.setName("MyAgenda");
agenda.setDate("current date");
long rowId = agendaManager.insert(agenda); // the guy who makes database operations
Cursor query = context.getContentResolver().query(AgendaProvider.AGENDA_CONTENT_URI, null, null, null, null);
assertThat(query.getCount()).isEqualTo(1);
query.moveToNext();
Agenda dbAgenda = new Agenda(query);
assertThat(dbAgenda.getRowId()).isPositive();
assertThat(dbAgenda.getRowId()).isEqualTo(rowId);
assertThat(dbAgenda.getName()).isEqualTo(agenda.getName());
assertThat(dbAgenda.getDate()).isEqualTo(agenda.getDate());
An more detailed example may be found here https://github.com/nenick/android-gradle-template/blob/master/UnitTestsRobolectric/src/test/java/com/example/managers/AgendaManagerTest.java
I initially created an SQLite database on Windows and then had problems accessing it within Android.
Subsequently I created a database on Android and then copied it out. At this point it only had the android_metadata table in it.
I then imported some data via CSV and added it back into my project. The DbHelper class in my project copies the database into /data/data/my.project/databases/.
Now, when I run a raw query from this database, if I try to access the table imported by CSV, I get an error saying that the table doesn't exist. If I try to access the android_metadata table which I created on Android then there is no error.
The database in my assets definitely has the table in that I wish to copy over to the /data/data/example.project/databases folder and the copy routine is definitely called - I've checked with the log output.
Now, if I comment out the copy code, a database is automatically created which contains the android_metadata table in there and it is ~3 KB.
When the copy code is live the database is created as ~8 KB. This is the size of the database in the assets, so it appears that it has been successfully copied. However, when I pull that database back to my desktop from DDMS it is ~8 KB, but it doesn't contain the table which is in the one in the assets folder. If I manually copy directly from desktop into /data/data... then the database works (but this will not be possible with a market app).
Here is my copy code for copying the database:
public void createDatabase() throws IOException {
Log.i(TAG, "createDatabase called");
InputStream assetsDB = mContext.getAssets().open(DATABASE_NAME);
OutputStream dbOut = new FileOutputStream(DATABASE_PATH);
Log.i(TAG, DATABASE_PATH);
Log.i(TAG, assetsDB.toString());
byte[] buffer = new byte[1024];
int length;
while ((length = assetsDB.read(buffer))>0) {
Log.i(TAG, "WritingDB block" + length);
dbOut.write(buffer, 0, length);
}
dbOut.flush();
dbOut.close();
assetsDB.close();
}
How can I fix this problem?
I've rectified this using another example which doesn't override onCreate with the database copy code and handles the copying of the database on its own. I don't really understand why it doesn't work when calling the onCreate method.
Have you seen Using your own SQLite database in Android applications?
This page is a good source for the topic. But, there is a little problem. Actually, it is not a problem and explained how to fix in the page. Look at comments.
If your database is sort of largish or smallish (> 1 MB, < 100 KB (I am not sure about these values)). It seems that it is compressed and that causes confusion in the Android read on the InputStream. The trick is to rename your asset to a file that the packager will not try to compress. Renaming the database file from xxx to xxx.mp3 or xxx.txt or something like that does the trick.
If I clearly understand you, I had the same problem with loading an SQLite database from other sources (I used to the Firefox SQLite manger too).
I want to read a temporary database from the assets folder at startup and fill my application database with test data, and I usually get this error.
I need to put this code before loading my test database:
final SQLiteDatabase db = getReadableDatabase();
db.close();
My database helper class:
public DataBaseHelper(Context context) {
super(context,
DataBaseHelper.DATABASE_NAME,
null,
DataBaseHelper.DATABASE_VERSION);
this.context = context;
// Temporary copy test database
loadMockDataBase();
dataBase = getWritableDatabase();
}
#Override
public final synchronized void close() {
if (dataBase != null) {
dataBase.close();
}
super.close();
}
private void loadMockDataBase() {
final SQLiteDatabase db = getReadableDatabase();
db.close();
try {
copyDataBase();
}
catch (final IOException e) {
Log.d(SystemConfiguration.LOG_TAG, e.getMessage());
}
}
I have created database AddressBookMaster.db using Sqlite Browser and also create table AddressBook in mentioned database. But now problem is that I want to use this .db file in my application so where should I put this db file and how to fetch in application?
Is there any example or tutorial which can help to me?
You can put your Database file in the SDcard also. Use following code to use the Database from the SDCard.
File dbfile = new File("/sdcard/Your_db_File.db" );
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
The information contained in Using your own SQLite database in Android applications should have exactly what you are looking for.
You should note that while this is not really difficult it is not just a set of trivial commands that would be done outside of your application.
The information contained in "Using your own SQLite database in Android applications" is very userful for U and If U have db with more than 1024 bytes size then first split db into small parts and copy that all small dbs in your application dirrectory .
Use android-sqlite-asset-helper, to fill (initialize) the database of your app on installation
If You want to change the default folder of the database to sdcard:
public class MyApplication extends Application {
#Override
public void onCreate() {
SQLiteOpenHelper helper = ..
SQLiteDatabase db = helper.getWritableDatabase()
}
#Override
public File getDatabasePath(String name) {
return new File(getDatabasePathString(name));
}
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
return super.openOrCreateDatabase(getDatabasePathString(name), mode, factory);
}
#Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
return super.openOrCreateDatabase(getDatabasePathString(name), mode, factory, errorHandler);
}
/*Put the default folder to store database of your application / or activity here */
public String getDatabasePathString(String name) {
return getExternalCacheDir()+ "/" + name+".db"; /* /storage/emulated/0/Android/data/com.my.app/cache */
}
}