What I'm trying to do
In the app I'm creating we're saving secret user data that needs to be encrypted. For that we'd like to use SQLCipher. We've implemented as in the documentation.
Now I'm facing following problem. If I create the database for the first time I can read and write the db. If I restart the app I'm unable to read the database again and get tons of errors. What am I doing wrong?
Here's the Code of our SQLiteOpenHeler:
public Database (Context ctx){
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
try{
mKeySaver = new KeySaver(ctx);
File databaseFile = ctx.getDatabasePath(Database.DATABASE_NAME);
if(databaseFile.exists()){
SQLiteDatabaseHook hook = new SQLiteDatabaseHook(){
public void preKey(SQLiteDatabase database){
database.rawExecSQL("PRAGMA kdf_iter = 5000");
}
public void postKey(SQLiteDatabase database){}
};
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, mKeySaver.getKey(), null, hook);
} else {
databaseFile.mkdir();
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, mKeySaver.getKey(), null);
mDb.setLocale(Locale.GERMAN);
}
} catch (Exception e){
Log.d("Database", "Exception " + e);
}
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
try{
sqLiteDatabase.execSQL(CREATE_TABLE_ACCOUNT);
sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_DETAILS);
sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_OVERVIEW);
sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_UPLOAD);
sqLiteDatabase.execSQL(CREATE_TABLE_ASSIGNMENT_USER);
sqLiteDatabase.execSQL(CREATE_TABLE_ACCOUNT);
}catch (Exception e){
Log.e("Database", "Exception " + e);
}
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
if (oldVersion >= newVersion) {
return;
}
// Falls ein DB-Update stattfinden sollte Code hier
}
And that's how we instantiate it:
SQLiteDatabase.loadLibs(mContext.getApplicationContext());
mKeySaver = new KeySaver(mContext);
mDb = new Database(mContext.getApplicationContext());
db_password = mKeySaver.getKey();
mDatabase = mDb.getWritableDatabase(db_password);
And the Logcatdump:
04-23 09:00:44.723 2405-2405/ivo E/Database﹕ CREATE TABLE android_metadata failed
04-23 09:00:44.726 2405-2405/ivo E/Database﹕ Failed to setLocale() when constructing, closing the database
net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
at net.sqlcipher.database.SQLiteDatabase.native_setLocale(Native Method)
at net.sqlcipher.database.SQLiteDatabase.setLocale(SQLiteDatabase.java:2092)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1958)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:875)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:907)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:197)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
at ivo.database.adapter.AccountAdapter.checkAccountPassword(AccountAdapter.java:79)
at ivo.activity.LoginActivity.attemptLogin(LoginActivity.java:168)
at ivo.activity.LoginActivity$2.onClick(LoginActivity.java:88)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19748)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
04-23 09:00:44.730 2405-2405/swisscom.ivocore E/SQLiteOpenHelper﹕ Couldn't open ivo.db for writing (will try read-only):
net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
at net.sqlcipher.database.SQLiteDatabase.native_setLocale(Native Method)
at net.sqlcipher.database.SQLiteDatabase.setLocale(SQLiteDatabase.java:2092)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1958)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:875)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:907)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:197)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
at ivo.database.adapter.AccountAdapter.checkAccountPassword(AccountAdapter.java:79)
at ivo.activity.LoginActivity.attemptLogin(LoginActivity.java:168)
at ivo.activity.LoginActivity$2.onClick(LoginActivity.java:88)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19748)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
你可能需要表ANDROID_METADATA
与(键,值)=('locale',你的LOCALE)
It look like you are opening the database again when your app restart.I had the same error but I solve it by creating a helper class which cache the previously opened database and only open it if it is really needed like below.
public class Helper {
private static net.sqlcipher.database.SQLiteDatabase database;
public static net.sqlcipher.database.SQLiteDatabase openDatabase(Context context, String databaseName, String password) {
if (database == null) {
database = net.sqlcipher.database.SQLiteDatabase .openOrCreateDatabase(context.getDatabasePath(context.getResources().getString(database)), password, null);
}
return database;
}
}
So when your app restart try to called openDatabase() method again.
This line looks weird to me:
databaseFile.mkdir();
mkdir()
creates a folder, hence you are passing a folder instead of a file to openOrCreateDatabase()
.
Also I think you shouldn't call openOrCreateDatabase()
in the SQLiteOpenHelper's constructor. It takes care of creating the database file automatically. Then you can get the SQLiteDatabase
object by calling for example SQLiteOpenHelper.getWritableDatabase(password)
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.