【问题标题】:SQLCipher change DB password with rekeySQLCipher 使用 rekey 更改数据库密码
【发布时间】:2014-12-10 11:36:51
【问题描述】:

我在 Android 上使用 SQLCipher 来获取加密数据库。数据库获得一个基于一些硬编码值的默认密码。用户还可以在应用程序上设置密码。当用户这样做时,我还想更改 SQLCipher 使用的数据库密码。我已经在 StackerOverflow 上找到了一些帖子说我应该使用 rekey。

目前我正在使用此代码,就像建议的帖子一样

final DatabaseHelper helper = new DatabaseHelper(this);
final SQLiteDatabase db = helper.getWritableDatabase(oldPassword);
final String PRAGMA_KEY = String.format("PRAGMA key = \"%s\";", oldPassword);
final String PRAGMA_REKEY = String.format("PRAGMA rekey = \"%s\";", newPassword);
db.rawExecSQL("BEGIN IMMEDIATE TRANSACTION;");
db.rawExecSQL(PRAGMA_KEY);
db.rawExecSQL(PRAGMA_REKEY);
db.close();

但是当我在密码应该更改后尝试插入数据库时​​,我得到了这个错误。

sqlite returned: error code = 26, msg = statement aborts at 1: [BEGIN EXCLUSIVE;] file is encrypted or is not a database
Failure 26 (file is encrypted or is not a database) on 0xb90048c0 when executing 'BEGIN EXCLUSIVE;'
FATAL EXCEPTION: IntentService[DBService]
    Process: com.example, PID: 26502
    net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: BEGIN EXCLUSIVE;
            at net.sqlcipher.database.SQLiteDatabase.native_execSQL(Native Method)
            at net.sqlcipher.database.SQLiteDatabase.execSQL(SQLiteDatabase.java:1831)
            at net.sqlcipher.database.SQLiteDatabase.beginTransactionWithListener(SQLiteDatabase.java:584)
            at net.sqlcipher.database.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:538)
            at com.example.db.Provider.bulkInsert(OurProvider.java:196)
            at android.content.ContentProvider$Transport.bulkInsert(ContentProvider.java:250)
            at android.content.ContentResolver.bulkInsert(ContentResolver.java:1268)
            at nl.qbusict.cupboard.ProviderCompartment.put(ProviderCompartment.java:158)
            at com.example.db.DBUtils.saveObjects(DBUtils.java:32)
            at com.example.services.DBService.getDataFromAPI(DBService.java:119)
            at com.example.services.DBService.onHandleIntent(DBService.java:48)
            at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.os.HandlerThread.run(HandlerThread.java:61)

我检查了更改前后的原始密码和新密码,它们都一样。我还尝试添加 db.rawExecSQL("END;");db.rawExecSQL("COMMIT;"); 但这没有帮助。当我更改密码时,我还看到error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt 的日志消息。不知道有没有关系?

public class OurProvider extends ContentProvider {
    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        synchronized (LOCK) {
            SQLiteDatabase db = getDatabase().getWritableDatabase(getPassword());
            final String table = getTableString(uri);

            db.beginTransaction();
            int rowsInserted = 0;
            try {
                for (ContentValues value : values) {
                    db.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_REPLACE);
                    rowsInserted++;
                }
                db.setTransactionSuccessful();
            } catch (Exception e) {
                Crashlytics.logException(e);
                Log.d(TAG, Log.getStackTraceString(e));
                rowsInserted = -1;
            } finally {
                db.endTransaction();
                if (rowsInserted > 0) {
                    getContext().getContentResolver().notifyChange(uri, null);
                }

            }
            return rowsInserted;
        }
    }

    private String getPassword() {
        final String password = Base64.encodeToString(OurApplication.getEncryptionKey().getEncoded(), Base64.DEFAULT);
        Log.e("SQLCipher_OurProvider", "SQLCipher password: " + password);
        return password;
    }

    private void initDb() {
        SQLiteDatabase.loadLibs(getContext());

        mDatabaseHelper = new DatabaseHelper(getContext());
    }

    public DatabaseHelper getDatabase() {
        if (mDatabaseHelper == null) {
            initDb();
        }
        return mDatabaseHelper;
    }
}



public class DBService extends IntentService {
    private void updatePasscode(Intent intent) {
        final SecretKey oldKey = OurApplication.getEncryptionKey();
        final String encryptedString = SecurePrefs.getEncryptedString(PrefKeys.ENC_CONFIRM);
        if (!TextUtils.isEmpty(encryptedString)) {
            String decryptedString = Crypto.decrypt(oldKey, encryptedString);
            // check if the oldkey can decrypt the confirmation string.
            if (BaseActivity.CONFIRM_STRING.equals(decryptedString)) {
                String pin = intent.getStringExtra(KEY_PASSCODE);
                if (pin.equals(PasscodeActivity.REMOVE_PIN)) {
                    pin = null;
                }
                final SecretKey newKey = SecurityUtil.generateKey(this, pin);
                final String accessToken = getUserAccessToken();
                final String refreshToken = SecurePrefs.getString(PrefKeys.USER_REFRESH_TOKEN);
                final String email = SecurePrefs.getString(PrefKeys.USER_ID);
                final String confirmEncrypted = SecurePrefs.getString(PrefKeys.ENC_CONFIRM);
                // set the newly generated string in the application.
                OurApplication.setEncryptionKey(newKey);

                // clear the old encrypted prefs. save the values with the new encryption key.
                SecurePrefs.clear();
                SecurePrefs.putString(PrefKeys.USER_ACCESS_TOKEN, accessToken);
                SecurePrefs.putString(PrefKeys.USER_REFRESH_TOKEN, refreshToken);
                SecurePrefs.putString(PrefKeys.USER_ID, email);
                SecurePrefs.putString(PrefKeys.ENC_CONFIRM, confirmEncrypted);

                // update de encryption key in the database.
                final String oldPassword = Base64
                        .encodeToString(oldKey.getEncoded(), Base64.DEFAULT);
                final String newPassword = Base64
                        .encodeToString(newKey.getEncoded(), Base64.DEFAULT);

                final String PRAGMA_KEY = String.format("PRAGMA key = \"%s\";", oldPassword);
                final String PRAGMA_REKEY = String.format("PRAGMA rekey = \"%s\";", newPassword);

                final DatabaseHelper helper = new DatabaseHelper(this);
                final SQLiteDatabase db = helper.getWritableDatabase(oldPassword);


                db.rawExecSQL("BEGIN IMMEDIATE TRANSACTION;");
                db.rawExecSQL(PRAGMA_KEY);
                db.rawExecSQL(PRAGMA_REKEY);
                db.close();
                sendBroadcast(IntentUtil.createBroadcastPasscodeUpdated());
            }
        }
    }
}

【问题讨论】:

标签: android sqlite encryption sqlcipher


【解决方案1】:

您不应该开始一个事务(这是通过internally 处理的,并且略有不同),您也不需要执行代码的PRAGMA key='…'; 部分。您可以通过调用getWritableDatabase(…); 打开数据库,然后执行PRAGMA rekey='…'; 命令。

【讨论】:

  • 我删除了 PRAGMA key='...';但它仍然无法正常工作,所以我只是简单地从数据库中获取所有数据并将其删除,然后使用新密钥创建一个新数据。
  • 我们有一个在 SQLCipher for Android 测试套件 here 中重新加密数据库的示例。
  • 嗨,尼克,我试过了,但没用 sqlite returned: error code = 26, msg = file is encrypted or is not a database CREATE TABLE android_metadata failed Failed to setLocale() when constructing, closing the database net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
  • 你运行测试套件了吗?
  • 对于任何访问此页面的人,我相信这是新链接HERE
【解决方案2】:

我也遇到了这个问题,原因是数据库一开始没有加密,所以PRAGMA key = <new_key>PRAGMA rekey = <new_key>失败了。所以,oldPassword 不能为 null 或为空。

【讨论】:

  • 谢谢我使用了初始密码:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-14
  • 2013-02-06
  • 2017-06-26
  • 1970-01-01
  • 1970-01-01
  • 2012-11-05
相关资源
最近更新 更多