首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SQLCipher for Android getReadableDatabase()过度宣传

SQLCipher for Android getReadableDatabase()过度宣传
EN

Stack Overflow用户
提问于 2014-03-04 15:42:40
回答 2查看 3.8K关注 0票数 4

我已经修改了我的DatabaseHelper类以使用SQLCipher库。

为此,我:

  • 将这些资产复制到我的资产文件夹中,并将库(armeabi、x86、commons、番石榴-R09、sqlcipher)复制到我的libs文件夹中。
  • 更改了我的DatabaseHelper类中的导入,使它们指向import net.sqlcipher.database.*
  • 当应用程序启动时打电话给SQLiteDatabase.loadLibs(getApplicationContext());
  • 修改我调用getReadableDatabase()getWriteableDatabase()的行,使它们包含一个密码作为参数;

当数据被正确读写时,一切似乎都正常。我的问题与性能有关,因为我的应用程序可能以某种频率执行DB操作,导致它变得缓慢(在迁移到SQLCipher之后)。

对于我的DatabaseHelper方法,我相信我遵循的是标准方法,例如:

代码语言:javascript
运行
复制
/*
 * Getting all MyObjects
 */
public List<MyObject> getMyObjects() {

    List<MyObject> objects = new ArrayList<MyObject>();

    String selectQuery = "SELECT * FROM " + TABLE_NAME;
    Log.v(LOG, selectQuery);

    // Open
    SQLiteDatabase db = this.getReadableDatabase("...the password...");  
    // I know this passphrase can be figured out by decompiling.

    // Cursor with query
    Cursor c = db.rawQuery(selectQuery, null);

    // looping through all rows and adding to list
    if (c.moveToFirst()) {
        do {
            MyObject object = createMyObjectFromCursor(c); // Method that builds MyObject from Cursor data
            // adding to list
            objects.add(object);
        } while (c.moveToNext());
    }

    c.close();
    db.close();
    return objects;
}

我并不完全熟悉SQLCipher的内部机制(例如,当我调用getReadableDatabase()时,它会解密整个DB文件吗?)但是,在调试时,开销似乎在getReadableDatabase(password)getWritableDatabase(password)中,如果上面的假设是正确的话,这是有意义的。

将这些调用移动到DatabaseHelper.open()和DatabaseHelper.close()方法(每当它们实例化DatabaseHelper时都会被它们调用),而不是在每个单独的方法上调用它们,这是否是一种糟糕的做法?请分享如何解决这一问题的知识。

编辑:

我已经使用DDMS来跟踪其中一个方法,并且我可以看到开销确实在SQLiteOpenHelper.getReadableDatabase()上(花费了大约4秒)。每次)。这些查询似乎运行得很快,我不认为我需要担心它们。

如果我向下钻取调用,每次跟踪持续时间最长的调用,我将得到以下结果:

SQLiteDatabase.OpenOrCreateDatabase -> SqLiteDatabase.openDatabase --> SQLiteDatabase.openDatabase --> SQLiteDatabase.setLocale

因此,SQLiteDatabase.setLocale(java.util.Locale)似乎是罪魁祸首,因为每次调用getReadableDatabase()都要花费大约4秒。我查看了SQLiteDatabase的源代码,它只锁定DB,调用native_setLocale(locale.toString(), mFlags) (4秒)。(开销发生在这里),并解锁DB。

知道为什么会这样吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-03-10 18:56:28

您所看到的性能问题很可能是由于SQLCipher密钥派生造成的。SQLCipher打开数据库的性能故意慢,使用PBKDF2执行密钥派生(即数千个SHA1操作),以抵御蛮力和字典攻击(您可以在http://sqlcipher.net/design上读到更多关于这一点的信息)。这个活动被推迟到数据库的第一次使用,这恰好发生在setLocale中,这就是为什么您在分析时会看到性能问题。

最好的选择是缓存数据库连接,这样就可以多次使用它,而不必重复打开和输入数据库。如果可能的话,在启动期间打开一次数据库是首选的操作方法。对同一个数据库句柄的后续访问不会触发密钥派生,因此性能将更快。

如果这是不可能的,则另一个选项是禁用或削弱密钥派生。这将导致SQLCipher在派生密钥时使用较少的PBKDF2轮。虽然这将使数据库更快地打开,但从安全性的角度来看,它要弱得多。因此,除例外情况外,不建议这样做。尽管如此,以下是关于如何减少KDF迭代的信息:

iter

票数 3
EN

Stack Overflow用户

发布于 2014-03-04 15:55:09

当我调用getReadableDatabase()时,它会解密整个DB文件吗?

不是的。它解密页(4KB?)根据需要,在飞行中。

开销似乎在getReadableDatabase(密码)和getWritableDatabase(密码)中,如果我上面的假设是正确的,这是有意义的。

只在整个过程中调用一次。任何其他问题都是不安全的,因为它要求您在任何开销问题之上保留密码。

当然,您似乎是在对密码进行硬编码,在这种情况下,所有这些加密都是毫无意义的,是浪费时间的。

请分享如何解决这一问题的知识。

使用Traceview确定您的时间是在哪里度过的。

在我执行的一个基准测试中--将一个SQLite基准转换为SQLCipher --我无法检测到任何物质开销。磁盘I/O淹没了加密开销,据我所知。

如果为Android应用程序编写良好的SQLCipher会增加开销,就会使糟糕的操作变得更糟。因此,例如,需要进行表扫描的查询已经很难执行了;SQLCipher会使它变得越来越难。解决方案是根据需要添加适当的索引(或FTS3),以避免表扫描。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22176445

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档