Encrypt Existing Database with SQLCipher in Android
Asked Answered
S

3

8

I have a database file in my assets that I copy in the app database folder. After the copy (it works), I want to encrypt the copied database with SQLCipher.

For some reason, I get this error:

Database: sqlite returned: error code = 26, msg = statement aborts at 5: [ATTACH DATABASE '/data/user/0/com.grandeguru.lagmeup/databases/AIRPORTS_DB.db' AS encrypted KEY 'password';] file is encrypted or is not a database

If I look the Database with a root explorer, it is still not encrypted and visible, so I suppose the error is related to the file logic inside the encrypt method.

This is the code of the DatabaseHelper class I created, that manages also the copy from assets:

public class DatabaseHelper extends SQLiteOpenHelper {

private SQLiteDatabase myDB;
private Context context;
public static String DB_NAME = "AIRPORTS_DB.db";
public static String DB_PATH = "/data/data/com.grandeguru.lagmeup/databases/";
public static final int DB_VERSION = 1;


public DatabaseHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    myDB.loadLibs(context);
    this.context = context;
}

// THE ERROR IS SOMEWHERE INSIDE HERE
public void encryptDataBase(String passphrase) throws IOException {

    File originalFile = context.getDatabasePath(DB_NAME);

    File newFile = File.createTempFile("sqlcipherutils", "tmp", context.getCacheDir());

    openDataBase("");

    myDB.rawExecSQL("ATTACH DATABASE '" + originalFile.getPath() + "' AS encrypted KEY '" + passphrase + "';");
    myDB.rawExecSQL("SELECT sqlcipher_export('encrypted');");
    myDB.rawExecSQL("DETACH DATABASE encrypted;");
    myDB.close();
    myDB = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READWRITE);
    myDB.close();
    newFile.renameTo(originalFile);
    originalFile.delete();
}

private boolean checkDataBase() {
    File databasePath = context.getDatabasePath(DB_NAME);
    return databasePath.exists();
}


public void copyDataBase() throws IOException {
    try {
        File f = context.getDatabasePath(DB_NAME);
        InputStream in = context.getAssets().open(DB_NAME);
        OutputStream out = new FileOutputStream(f.getAbsolutePath());
        byte[] buffer = new byte[1024];
        int length;
        while ((length = in.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }
        out.flush();
        out.close();
        in.close();
        Log.d("copy database", "finita copia db");

        encryptDataBase("password");
    } catch (Exception e) {
        Log.e("copy database", e.getMessage());
    }

}


public void openDataBase(String passphrase) throws SQLException {
    String myPath = DB_PATH + DB_NAME;
    myDB = SQLiteDatabase.openDatabase(myPath, passphrase, null, SQLiteDatabase.OPEN_READWRITE);
}


public void createDataBase() throws IOException {
    boolean dbExist = checkDataBase();

    if (dbExist) {
    } else {
        this.getReadableDatabase("");
        try {
            copyDataBase();
        } catch (IOException e) {
            Log.e("create database", e.getMessage());
        }
    }
}

}

Stradivari answered 14/9, 2016 at 16:50 Comment(0)
S
5

I solved, I write my solution as future reference. I was simply getting the wrong path

public void encryptDataBase(String passphrase) throws IOException {

    File originalFile = context.getDatabasePath(DB_NAME);

    File newFile = File.createTempFile("sqlcipherutils", "tmp", context.getCacheDir());

    SQLiteDatabase existing_db = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, "", null, SQLiteDatabase.OPEN_READWRITE);

    existing_db.rawExecSQL("ATTACH DATABASE '" + newFile.getPath() + "' AS encrypted KEY '" + passphrase + "';");
    existing_db.rawExecSQL("SELECT sqlcipher_export('encrypted');");
    existing_db.rawExecSQL("DETACH DATABASE encrypted;");

    existing_db.close();

    originalFile.delete();

    newFile.renameTo(originalFile);

}
Stradivari answered 15/9, 2016 at 14:17 Comment(0)
S
1

We have an example of encrypting a plain text SQLite database within the SQLCipher for Android test suite. Also, you might try calling the static SQLiteDatabase.loadLibs(context); instead of trying to call it on your instance field.

Shimkus answered 15/9, 2016 at 13:15 Comment(3)
It was an error of path, SQLCipher was doing it right ;) Now I evolved my DatabaseHelper class in order to manage a database not encrypted ("" pass) and an encrypted one tooStradivari
The link you provided is gone forever :(. Do you have any tutorial for encrypting a sql database using sqlcipher? not just in android but in windows command line or any GUI.Separates
Fixed link: Import Unencrypted Database TestNeediness
P
0

For some reason the DB encryption you can find around the internet seems not to be universal. I've met this exception while trying to use it:

net.sqlcipher.database.SQLiteException: file is not a database

as described in this post

Problem is with File.createTempFile() method. You should use new File() instead. For accepted solution it might be

val newFile = File(context.cacheDir, "sqlcipherutils.tmp")
newFile.createNewFile()

Edit: Also make sure your passphrase doesn't contain ' ;)

Prepossess answered 3/8, 2022 at 17:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.