首页 > 解决方案 > 如何检查房间数据库中的数据是否加密?

问题描述

我正在构建一个使用房间数据库的应用程序,并使用 SQLCipher 对其进行加密。我如何检查数据是否真的加密,因为这是我第一次使用加密,我不知道我是否正确使用。

标签: androidkotlinandroid-roomsqlciphersqlcipher-android

解决方案


假设您要检查数据库是否在应用程序中加密,那么您必须小心一点。

建议通过房间打开数据库而不使用密钥进行检查,如果数据库被加密,将导致数据丢失。

  • 这是因为当房间在加密时尝试打开数据库时,它将被视为损坏的数据库,因为整个文件都已加密,因此不是有效的数据库文件(标题字符串不是必需的标题字符串“SQLite 格式 3\ 000”)。Room 然后将删除损坏的数据库,然后调用 onCreate 方法来提供一个没有数据的有效新数据库。

  • 请参阅当房间 android db 损坏时会发生什么?

我建议您考虑打开文件,而不是作为数据库,而是作为文件并检查前 16 个字节是否为“SQLite 格式 3\000”。

如果是“SQLite format 3\000”那么数据库没有被加密,如果不是“SQLite format 3\000”那么数据库(如果文件实际上是一个/数据库文件)可能被加密了。

也许考虑以下内容:-

    /**
     * Check to see if the database is encrypted
     *
     * NOTE assumes encrypted if the database header string is not the required SQLite header string
     * WARNING any invalid file would be considered encrypted
     */
    fun isEncryptedDatabase(context: Context, databaseName: String): Boolean {
        var rv = false

        // SQlite header String 16 bytes as SQLite format 3\000
        //  hex representation is  53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00
        val requiredSQLiteFileHeaderString: ByteArray
            = byteArrayOf(0x53,0x51,0x4c,0x69,0x74,0x65,0x20,0x66,0x6F,0x72,0x6D,0x72,0x61,0x74,0x20,0x33,0x0)
        var dbFileHeader = ByteArray(requiredSQLiteFileHeaderString.size)
        var i: InputStream
        try {
            i = FileInputStream(context.getDatabasePath(databaseName))
            i.read(dbFileHeader)
            if(!dbFileHeader.equals(requiredSQLiteFileHeaderString)) {
                rv = true
            }
            i.close()
        } catch (e: IOException) {
            rv = true;
        }
        return rv
    }
}

您可以在 Room 之外和 Room 打开数据库之前使用它。考虑到上面使用了 Context 的 getDatabasePath 方法,那么真实结果(数据库被加密)很可能是由于数据库被加密而不是另一种类型的文件。

这是使用嵌入在 @Database 类中的上述代码的示例(基于使用用于上述链接答案的代码)。活动代码是:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        if (!TheDatabase.isEncryptedDatabase(this,TheDatabase.DATABASENAME)) {
            //.... handle un-encrypted database here
        }
        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()
        .... 
    }
}
  • 注意为了方便和简洁,上面使用了主线程(.allowMainThreadQueries()即已在 databaseBuilder 中编码)

推荐阅读