【问题标题】:Converting sqlite to encrypted database:将 sqlite 转换为加密数据库:
【发布时间】:2014-07-24 10:54:25
【问题描述】:

我的应用程序中有一个现有的 sqlite 数据库。它运行成功。现在我需要使用 sqlcipher 加密数据库。我的问题是在将 sqlite 转换为加密时出现以下异常。

net.sqlcipher.database.SQLiteException: table android_metadata already exists
 at net.sqlcipher.database.SQLiteDatabase.native_rawExecSQL(Native Method)
 at net.sqlcipher.database.SQLiteDatabase.rawExecSQL(SQLiteDatabase.java:1851)
 at com.x.y.convert_sqlite_to_sqlcipher(Practitioner_menu.java:2626)
 at com.x.y$AdminProcess.doInBackground(Practitioner_menu.java:1659)
 at com.x.y$AdminProcess.doInBackground(Practitioner_menu.java:1)
 at android.os.AsyncTask$2.call(AsyncTask.java:287)
 at java.util.concurrent.FutureTask.run(FutureTask.java:234)
 at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
 at java.lang.Thread.run(Thread.java:856)

我的转换代码:

File old_sqliteFile = getDatabasePath("old_db.sqlite");
File databaseFile = getDatabasePath("new_db.db");
        databaseFile.mkdirs();
        databaseFile.delete();
    database = SQLiteDatabase.openOrCreateDatabase(databaseFile,
                    "password", null);
database.rawExecSQL(String.format(
                    "ATTACH DATABASE '%s' AS encrypted KEY '%s'",
                    databaseFile.getAbsolutePath(), "password");
            database.rawExecSQL("select sqlcipher_export('encrypted')");
            database.rawExecSQL("DETACH DATABASE encrypted");
            database.close();

请任何人帮助我找出我做错了什么?

【问题讨论】:

标签: android sqlite sqlcipher


【解决方案1】:

您使用了两次databaseFile。你的openOrCreateDatabase() 应该被称为old_sqliteFile,而不是databaseFile

这是一种将未加密的数据库文件替换为加密替换的方法:

  public static void encrypt(Context ctxt, String dbName,
                             String passphrase) throws IOException {
    File originalFile=ctxt.getDatabasePath(dbName);

    if (originalFile.exists()) {
      File newFile=
          File.createTempFile("sqlcipherutils", "tmp",
                              ctxt.getCacheDir());
      SQLiteDatabase db=
          SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
                                      "", null,
                                      SQLiteDatabase.OPEN_READWRITE);

      db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
                                  newFile.getAbsolutePath(), passphrase));
      db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
      db.rawExecSQL("DETACH DATABASE encrypted;");

      int version=db.getVersion();

      db.close();

      db=
          SQLiteDatabase.openDatabase(newFile.getAbsolutePath(),
                                      passphrase, null,
                                      SQLiteDatabase.OPEN_READWRITE);
      db.setVersion(version);
      db.close();

      originalFile.delete();
      newFile.renameTo(originalFile);
    }
  }

【讨论】:

  • 我有一个问题。在任何情况下,下面的代码都失败了 db.rawExecSQL("SELECT sqlcipher_export('encrypted')");我不能做任何进一步的数据库损坏。如何处理。如果这组代码再次运行仍然没有运气。根据您的回答,stackoverflow.com/questions/22062277/… 我认为这已损坏。
  • 有什么简单的方法可以查看数据库是否已经加密? if (db.isEncrypted()){ db.encrypt(); }?
  • @user2676468:不,抱歉。
  • 那么你会如何推荐这个 encrypt() 调用呢?我会得到一个错误还是只在 DB onUpgrade 调用中执行此操作?
  • @user2676468:你不能在onUpgrade() 调用SQLiteOpenHelper中这样做,因为数据库已经在那里打开了。虽然没有 db.isEncrypted() 选项,但如果您可以使用常规 SQLite(或使用 SQLCipher 但使用空密码)打开数据库,则您知道数据库加密。当您有一个未加密的数据库并且用户表示她想要切换到一个加密的数据库时,您知道何时调用encrypt()
【解决方案2】:

谢谢,效果很好。 我添加了这段代码来加密、解密和更改密码。

@Override
public synchronized SQLiteDatabase getWritableDatabase(String password) {
    SQLiteDatabase sqLiteDatabase;
    try{
        sqLiteDatabase = super.getWritableDatabase(password);
    }catch(Exception e){
        try {
            encrypt(context,DATABASE_NAME,PWOLD,PWNEW);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        sqLiteDatabase = super.getWritableDatabase(password);
    }
    return sqLiteDatabase;
}

@Override
public synchronized SQLiteDatabase getReadableDatabase(String password) {
    SQLiteDatabase sqLiteDatabase;
    try{
        sqLiteDatabase = super.getReadableDatabase(password);
    }catch(Exception e){
        try {
            encrypt(context, DATABASE_NAME, PWOLD, PWNEW);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        sqLiteDatabase = super.getReadableDatabase(password);
    }
    return sqLiteDatabase;
}

public static void encrypt(Context context, String dbName,
                           String oldPass, String newPass) throws IOException {
    if(!newPass.isEmpty()) Log.d(TAG,"Encrypt DB...");
    else Log.d(TAG,"Decrypt DB...");

    File originalFile=context.getDatabasePath(dbName);

    if (originalFile.exists()) {
        File newFile = File.createTempFile("sqlcipherutils", "tmp",
                        context.getCacheDir());
        SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
                oldPass, null,
                SQLiteDatabase.OPEN_READWRITE);

        db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
                newFile.getAbsolutePath(), newPass));
        db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
        db.rawExecSQL("DETACH DATABASE encrypted;");

        int version=db.getVersion();

        db.close();

        db = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(),
                newPass, null,
                SQLiteDatabase.OPEN_READWRITE);
        db.setVersion(version);
        db.close();

        originalFile.delete();
        newFile.renameTo(originalFile);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-13
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多