【问题标题】:Is there a correct way to test whether a user-provided SQLCipher password is valid on Android?是否有正确的方法来测试用户提供的 SQLCipher 密码在 Android 上是否有效?
【发布时间】:2013-05-09 07:46:10
【问题描述】:

我正在使用SQLCipher for Android 并试图确定测试用户提供的密码是否有效的正确方法。

我的第一个想法是尝试使用给定密码打开数据库,使用SQLiteOpenHelper.getReadableDatabase(password) 的 SQLCipher 实现,然后捕获弹出的SQLiteException

这确实有效,但问题是因为 Android API 实际上封装了底层 C 调用,它为您做了很多工作 - 具体来说,当您使用 Android API 打开数据库时,它会打开数据库,运行原生 C 级 sqlite3_key 方法(使用提供的密码),然后尝试在数据库上设置区域设置,无论提供的密码是否正确。

此时Android库尝试设置locale,底层数据库抛出“加密还是不是数据库”SQLiteException,被捕获并重新抛出;但在此之前,一个不相关的错误被写入日志,本质上是说无法设置区域设置并且数据库正在关闭(包含堆栈跟踪)。因为这是由 Android 库专门编写的,所以我无法抑制它,在日志中留下一个丑陋的错误,这实际上与我原来的问题无关,只是我输入了错误的密码。

由于Android库不暴露C级调用,我不能只使用SQLCipher API文档中关于Testing the Key描述的方法,因为我没有权限直接打开数据库。

我倾向于使用SQLiteDatabaseHook,但据我所知,这排除了我使用SQLiteOpenHelper 的可能性,它似乎没有提供设置挂钩的方法。

还有其他人知道测试输入密码是否通过 SQLCipher Android API 正确解密 SQLCipher 数据库的更好方法吗?我完全希望调用一个方法并检查是否引发了异常 - 我不希望该操作尝试在数据库上执行无关处理(如设置区域设置)并将一个完全无法抑制的错误写入我的日志。

【问题讨论】:

  • "...这实际上与我原来的问题无关,只是我输入了错误的密码" -- 不,这是原来的问题:你输入了错误的密码,这这就是 set-locale 操作失败的原因。 “我不能只使用 SQLCipher API 文档中描述的关于测试密钥的方法......” - 您的结果将与您目前得到的结果相同,除了在 LogCat 中可能没有任何无关的痕迹。除此之外,您对“任何更好的方法”的标准是什么?
  • 很抱歉,我显然没有说清楚。我要做的就是检查我是否输入了错误的密码。我完全同意我的结果是一样的(打电话,检查错误)。我对“任何更好的方法”的标准是没有多余的 setLocale 处理和错误。
  • 我只是提出一个问题,让他们不记录错误并且不用担心它。除此之外,我推荐 sqlcipher Google Group 以获得更多帮助。
  • 看起来您在这里问了基本相同的问题:github.com/sqlcipher/android-database-sqlcipher/issues/76,但也没有得到您满意的答案。

标签: android sqlcipher


【解决方案1】:

SQLCipher for Android 不知道您提供的密码在sqlite3_key 调用之后无效,因为在对sqlite3_key 之后的数据库发出 SQL 命令之前,不会使用数据库密钥,例如setLocale(...) 您在上面引用的方法。问题是,提供无效键可能只是在执行第一个 SQL 语句时可能出现问题的其他可能场景之一。损坏的数据文件、失败的 HMAC 检查或打开非数据库文件都可能导致同样的错误消息。有关这方面的详细说明,请查看此thread。最好在尝试打开数据库时捕获异常并在客户端应用程序中进行相应处理。

【讨论】:

  • 我绝对明白。我遇到的问题是我无法像我的问题那样复制“测试密钥”。通过 iOS API,我可以访问 C API,因此我可以打开数据库 pragma key,然后尝试从 sqlite_master 读取。如果这引发错误,我可以抓住它并收工。但是,通过 Android API,我不能简单地做到这一点,因为我必须调用 openOrCreateDatabase 打开数据库,键入它,然后尝试设置区域设置,这就是错误发生的地方。这还不错,但无法抑制的日志输出令人沮丧。
【解决方案2】:

我没有更好的测试方法。我只是想另外提供一些代码,所以当其他人查找时,他们可能会发现一些有用的代码sn-ps。 至少当我发现这个问题时,我很想看到一些代码如何进行检查:)

我在android中的做法是这样的:

    // Simply get an instance of SQLiteOpenHelper.
    dbHelperObj = myDatabase.getInstance(this, str_username, version);

    // Now we try to open the database with the password from the user.
    try {
        dbObj = dbHelperObj.getReadableDatabase(str_password);

    // The only possible error now must be a wrong password.
    } catch (Exception e) {
        dbHelperObj.close();

        // Do stuff to tell the user he provided a wrong password.
    }

【讨论】:

  • 由于目前没有更好的解决方案,这被认为是一个可行的解决方案。对所有想要使用它的人来说,如果你抓住来自import net.sqlcipher.database.SQLiteExceptionSQLiteExceptions 会更准确。
【解决方案3】:

目前,验证密码的唯一方法是对您的数据库进行操作并检查它是否会抛出 net.sqlcipher.database.SQLiteException。所以对于那些使用带有 Room ORM 的 SQLCipher 的人来说,下面的代码应该可以完成这项工作。

使用提供的密码获取 Room 数据库实例后,调用 isPassphraseValid() 函数并将您的数据库实例传递给它。

    /* Passphrase validation: */
    private fun isPassphraseValid(db: MyRoomDatabase): Boolean {
        return try {
            db.openHelper.readableDatabase
            Log.d("sql-log", "Pass phrase is valid.")
            true
        } catch (e: net.sqlcipher.database.SQLiteException) {
            e.printStackTrace()
            Log.d("sql-log", "Pass phrase is not valid.")
            false
        }
    }

【讨论】:

    猜你喜欢
    • 2011-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 2015-02-08
    • 2022-01-10
    • 2012-02-20
    相关资源
    最近更新 更多