I have the following SqlCipher DB. It works fine when i first install the app, but if i close, remove the app from recent history(off the stack) and re-open the app it crashes with the following error. The db will not open once an exception is encountered, for example i put a 1/0 in an Activity to force it to crash and the same happened below.
07-20 15:39:05.669: E/Database(21425): CREATE TABLE android_metadata failed
07-20 15:39:05.669: E/Database(21425): Failed to setLocale() when constructing, closing the database
07-20 15:39:05.669: E/Database(21425): net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
.
I've found the following link that seems to fix the problem (i'm not sure if it is the solution) but i'm not sure how to implement it into my code. Could anyone help please or tell me why i'm getting this error?
http://rootslash.net/88542/sqlcipher-cant-open-database-after-apprestart
This is my DB code, i use SqlCipher SQLiteOpenHelper to create the DB. I think i need to modify this code so it returns a DB object if one already exists and creates one if it doesn't exist. i'm just not sure how.
Thanks in advance.
[EDIT1] I have a LoginActivity that verifies the user's credentials. if they are valid it loads the MenuActivity in which i have put a 1/0 to force a crash. After the crash if i re-open the app it crashes in the LoginActivity at the line where it queries the DB User table.
DBModel is a class that has the SqliteOpenHelper and CRUD methods.
So it is crashing at checkUserInDB() in the DBModel class, which in turn calls queryAllFromUser().
07-25 13:45:43.043 10654-10654/? E/AppObj﹕ Build.SERIAL = SH43PWM07311
07-25 13:45:43.203 10654-10654/? E/AppObj﹕ secretKey = com.android.org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey@31a79cea
07-25 13:45:43.643 10654-10654/? E/Database﹕ CREATE TABLE android_metadata failed
07-25 13:45:43.643 10654-10654/? 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:2096)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1962)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:881)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:913)
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 devreach.co.uk.devreach.DBModel.queryAllFromUser(DBModel.java:175)
at devreach.co.uk.devreach.DBModel.checkIfUserInDB(DBModel.java:262)
at devreach.co.uk.devreach.LoginActivity.onCreate(LoginActivity.java:62)
at android.app.Activity.performCreate(Activity.java:5958)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
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:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
07-25 13:45:43.643 10654-10654/? E/SQLiteOpenHelper﹕ Couldn't open devreach.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:2096)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1962)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:881)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:913)
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 devreach.co.uk.devreach.DBModel.queryAllFromUser(DBModel.java:175)
at devreach.co.uk.devreach.DBModel.checkIfUserInDB(DBModel.java:262)
at devreach.co.uk.devreach.LoginActivity.onCreate(LoginActivity.java:62)
at android.app.Activity.performCreate(Activity.java:5958)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
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:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
07-25 13:45:43.983 10654-10654/? E/Database﹕ CREATE TABLE android_metadata failed
07-25 13:45:43.983 10654-10654/? 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:2096)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1962)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:881)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:940)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:219)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
at devreach.co.uk.devreach.DBModel.queryAllFromUser(DBModel.java:175)
at devreach.co.uk.devreach.DBModel.checkIfUserInDB(DBModel.java:262)
at devreach.co.uk.devreach.LoginActivity.onCreate(LoginActivity.java:62)
at android.app.Activity.performCreate(Activity.java:5958)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
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:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
07-25 13:45:43.983 10654-10654/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: devreach.co.uk.devreach, PID: 10654
java.lang.RuntimeException: Unable to start activity ComponentInfo{devreach.co.uk.devreach/devreach.co.uk.devreach.LoginActivity}: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2411)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
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:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
Caused by: 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:2096)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1962)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:881)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:940)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:219)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:184)
at devreach.co.uk.devreach.DBModel.queryAllFromUser(DBModel.java:175)
at devreach.co.uk.devreach.DBModel.checkIfUserInDB(DBModel.java:262)
at devreach.co.uk.devreach.LoginActivity.onCreate(LoginActivity.java:62)
at android.app.Activity.performCreate(Activity.java:5958)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
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:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
LoginActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
appObj = (AppObj)getApplication();
editTextFirstName = (EditText)findViewById(R.id.edittextfirstname);
editTextPassword = (EditText)findViewById(R.id.editTextpassword);
String firstName = appObj.dbModel.checkIfUserInDB();
if(! firstName.equalsIgnoreCase("NO_USER")){
editTextFirstName.setText(firstName);
}
DBModel:
import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import android.content.ContentValues;
import android.content.Context;
import android.provider.BaseColumns;
import android.util.Log;
import android.widget.Toast;
public class DBModel {
private static final String TAG = DBModel.class.getSimpleName();
// table user column names
public static final String C_USER_ID_INDEX = BaseColumns._ID;
public static final String C_USER_ID = "userid";
public static final String C_USER_COMP_ID = "usercompid";
public static final String C_USER_FIRSTNAME = "userfirstname";
public static final String C_USER_LASTNAME = "userlastname";
public static final String C_USER_PASSWORD = "userpassword";
public static final String C_USER_DATE_TIME = "userdatetime";
// table company column names
public static final String C_COMPANY_ID_INDEX = BaseColumns._ID;
public static final String C_COMPANY_ID = "companyid";
public static final String C_COMPANY_NAME = "companyname";
public static final String C_COMPANY_URL = "companyurl";
public static final String C_COMPANY_GUID = "companyguid";
Context context;
DBHelper dbhelper;
AppObj appObj;
public DBModel(Context context) {
this.context = context;
dbhelper = new DBHelper();
appObj = (AppObj) context.getApplicationContext();
}
/**
* inner class to create/open/upgrade database
*
* @author matt
*
*/
private class DBHelper extends SQLiteOpenHelper {
// database name and version number
public static final String DB_NAME = "devreach.db";
public static final int DB_VERSION = 1;
// table names
public static final String TABLEUSER = "user";
public static final String TABLECOMPANY = "company";
public DBHelper() {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.e(TAG, "SQLiteOpenHelper oncreate ");
String sqlToCreateUserTable = String
.format("create table %s ( %s INTEGER primary key, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT)",
TABLEUSER, C_USER_ID_INDEX, C_USER_ID, C_USER_COMP_ID,
C_USER_FIRSTNAME, C_USER_LASTNAME, C_USER_PASSWORD,
C_USER_DATE_TIME);
db.execSQL(sqlToCreateUserTable);
Log.e(TAG, "oncreate " + sqlToCreateUserTable);
String sqlToCreateCompanyTable = String
.format("create table %s ( %s INTEGER primary key, %s TEXT, %s TEXT, %s TEXT, %s TEXT)",
TABLECOMPANY, C_COMPANY_ID_INDEX, C_COMPANY_ID, C_COMPANY_NAME,
C_COMPANY_URL, C_COMPANY_GUID);
db.execSQL(sqlToCreateCompanyTable);
Log.e(TAG, "oncreate " + sqlToCreateCompanyTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}//end of onUpgrade
}//end of DBHelper
public void close() {
dbhelper.close();
}
public void deleteTableUser() {
// open database
SQLiteDatabase db = dbhelper.getWritableDatabase(AppObj.getSecretKey().toString());
// delete contents of table
db.delete(DBHelper.TABLEUSER, null, null);
}
public void insertIntoUser(ContentValues cv) {
SQLiteDatabase db = dbhelper.getWritableDatabase(AppObj.getSecretKey().toString());
db.insertWithOnConflict(DBHelper.TABLEUSER, null, cv, SQLiteDatabase.CONFLICT_REPLACE);
}
public Cursor queryAllFromUser() {
// open database
SQLiteDatabase db = dbhelper.getReadableDatabase(AppObj.getSecretKey().toString());
return db.query(DBHelper.TABLEUSER, null, null, null, null, null, null);
}
public void deleteTableCompany() {
// open database
SQLiteDatabase db = dbhelper.getWritableDatabase(AppObj.getSecretKey().toString());
// delete contents of table
db.delete(DBHelper.TABLECOMPANY, null, null);
}
public void insertIntoCompany(ContentValues cv) {
SQLiteDatabase db = dbhelper.getWritableDatabase(AppObj.getSecretKey().toString());
db.insertWithOnConflict(DBHelper.TABLECOMPANY, null, cv, SQLiteDatabase.CONFLICT_REPLACE);
}
public Cursor queryAllFromCompany() {
// open database
SQLiteDatabase db = dbhelper.getReadableDatabase(AppObj.getSecretKey().toString());
return db.query(DBHelper.TABLECOMPANY, null, null, null, null, null, null);
}
public String getCompanyGuid(){
String guid = null;
Cursor c = null;
SQLiteDatabase db = dbhelper.getReadableDatabase(AppObj.getSecretKey().toString());
c = db.query(DBHelper.TABLECOMPANY, null, null, null, null, null, null);
if(c != null){
if(c.moveToLast()){
guid = c.getString(c.getColumnIndex(DBModel.C_COMPANY_GUID));
}
}
try{
c.close();
}catch(Exception e){}
return guid;
}
public String getCompanyID(){
String id = null;
Cursor c = null;
SQLiteDatabase db = dbhelper.getReadableDatabase(AppObj.getSecretKey().toString());
c = db.query(DBHelper.TABLECOMPANY, null, null, null, null, null, null);
if(c != null){
if(c.moveToLast()){
id = c.getString(c.getColumnIndex(DBModel.C_COMPANY_ID));
}
}
try{
c.close();
}catch(Exception e){}
return id;
}
public String checkIfUserInDB() {
String firstName = null;
Cursor c = queryAllFromUser();
if(c != null && c.getCount() > 0){
if(c.moveToLast()){
Log.e(TAG,"c != null and > 0");
firstName = c.getString(c.getColumnIndex(DBModel.C_USER_FIRSTNAME));
}
}else{
Log.e(TAG,"c == null");
firstName = "NO_USER";
}
try{
c.close();
}catch(Exception e){}
Log.e(TAG,"firstName = " + firstName);
return firstName;
}
}//end of DBModel
[EDIT2]
@Override
public void onCreate() {
super.onCreate();
secretKey = null;
Log.e(TAG, "Build.SERIAL = " + Build.SERIAL);
SecureRandom secureRandom = new SecureRandom();
byte[] salt = secureRandom.generateSeed(256);
try {
secretKey = generateKey(Build.SERIAL.toCharArray(), salt);
Log.e(TAG, "key-Base64 before in appObj = "+new String(Base64.encode(RROnCallApplication.getSecretKey().getEncoded(),0)));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e(TAG, "secretKey = " + secretKey);
SQLiteDatabase.loadLibs(this);
dbModel = new DBModel(this);
webService = new WebService(this);
alertCount = 0;
// Cursor checkCarerTable = dbModel.queryAllFromCarer();
//
// if(checkCarerTable.getCount() == 0){
//
// //runGetCarersService();
// //runGetClientsService();
//
//
// }else{
//
// Log.e(TAG, "carer and client table is populated with some data");
// }
}
private static SecretKey generateKey(char[] passphraseOrPin, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase
// computation time. You should select a value that causes computation
// to take >100ms.
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 256;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
The salt must be the same as well as the variables that you are hashing to create a key.
The variables assumably will remain the same, same user/user password, however your code is regenerating a random salt.
If you have encrypted the database with a key, the key has to be the same, and therefore the salt must be the same.
I hope this helps!
The error i was getting were because the key what getting regenerated with another SecureRandom for the salt. So after a crash the keys did not match. Mark has suggested using an unsalted user password which the user must enter when first using the app. Also the app must hold on to the SqlOpenHelper object which in turn holds on to the DB and key.
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