CREATE TABLE android_metadata failed when re-opening sqlcipher DB
Asked Answered
R

2

11

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;
}
Race answered 20/7, 2015 at 14:48 Comment(14)
"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." -- SQLiteOpenHelper already does that. Where and how are you creating your SQLiteOpenHelper instance, and where and how are you calling getReadableDatabase() or getWriteableDatabase() on it that is triggering this crash?Underwear
@Underwear I've added as much info as i can in EDIT 1. ThanksRace
@Underwear I thought i might add, i create the key to the DB in the onCreate method of the application object by passing the Build.SERIAL to a method that generates the key with a random salt. I'm just checking that after a crash the application object's onCreate doesn't run again and generate a different key? just a thought.Race
Well, your code has a variety of issues (e.g., disk I/O on the main application thread). I don't know where you are creating dbModel, but since you are holding onto it in static scope, the Context that it holds onto needs to be the Application instance (AppObj instance in your case). Anything else would not only result in a memory leak but could cause problems when you try using a defunct Context later on. Using SQLCipher here is a waste of time, as your key generation is trivially reverse-engineered, so you might consider rolling back to plain SQLite.Underwear
Beyond that, confirm that getSecretKey() is returning the right value in the post-exception case, try switching from getReadableDatabase() to getWriteableDatabase(), and see if the database file exists before your crash.Underwear
@Underwear I think what seems to be happening is when the app crashes my Appliction Object is generating a new SecretKey. i checked the value on install and re-opening app after a crash; They were different. Where is the best place to store the Secret Key, in SharedPreferences?Race
@Underwear To answer the questions above, i'm creating the dbModel class in the AppObj. I followed the Marakana video from about 5 yrs ago. Marko suggested 'hanging the DB off the Appliction Object'. So i've designed all my apps this way. i hope i'm correct in doing so? with regards to disk I/O on the UIthread, do you mean the lack of ContentProvider/CursorLoader? I've just started this project and haven't got around to designing these yet :)Race
@Underwear i have found a post from last year where you state that the user should keep the password in their head. The password on the DB would be derived from the login password. in Edit2 i have posted the appObj and how i generate the password. If i were to use the login password, i use a SecureRandom when generating the salt. would this not generate a different key each time?Race
"Where is the best place to store the Secret Key" -- in the user's head. SQLCipher is not a DRM solution. "i hope i'm correct in doing so?" -- there is nothing intrinsically wrong with it; just make sure you are using Application as your Context. "do you mean the lack of ContentProvider/CursorLoader?" -- whether you use that or some other solution is up to you, but you need to get the disk I/O off the main application thread.Underwear
"If i were to use the login password, i use a SecureRandom when generating the salt. would this not generate a different key each time?" -- yes. You do not need to randomly generate a salt here.Underwear
@Underwear ok thanks Mark, it is becoming a little clearer now. The last thing i'm unsure of is if i don't store the password in memory and only in the user's head, how can subsequent calls to the DB be made when the password is needed. Surely the login password needs to be store somewhere in memory then cleared down when the app is exited?Race
"Surely the login password needs to be store somewhere in memory then cleared down when the app is exited?" -- no. Just hold onto your SQLiteOpenHelper. It, in turn, holds onto the SQLiteDatabase. Only when you close() the SQLiteOpenHelper (or your process terminates) do you have to worry about supplying a passphrase to SQLCipher again, when your process starts up again. And, at that point, you ask the user for the passphrase again.Underwear
@Underwear great thanks a lot for your help. If you would like to make an answer as it was the passwords that didn't match, i'll accept your answer.Race
I would recommend that you post your own answer, explaining where things went wrong in your secret key generation. That would be more useful that simply my rehash of this comment thread.Underwear
T
4

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!

Tremain answered 30/7, 2015 at 12:29 Comment(0)
R
3

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.

Race answered 27/7, 2015 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.