简体   繁体   中英

Encrypt Existing Database with SQLCipher in Android

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());
        }
    }
}

}

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);

}

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

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 ' ;)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM