Android 12下Room数据库加密首次访问Dao触发file is not a database报错
问题根因
- 核心问题出在密钥生成逻辑的错误实现:你调用
generateSymmetricKey("mykey").toString()作为数据库加密密码,但Android 12对AndroidKeyStore返回的Key对象的toString()方法实现做了修改,每次应用重启后返回的字符串内容不一致。首次运行时用当前生成的字符串加密数据库,重启后解密时拿到的密码和加密时不一致,SQLCipher无法解密数据库就会抛出file is not a database错误。 - 次要兼容问题:你当前使用的Room 2.3.0版本发布时Android 12尚未正式推送,存在部分Android 12适配漏洞。
修复方案
1. 修正密钥获取逻辑
不要使用Key对象的toString()结果作为密码,直接取AES密钥的原始二进制字节作为加密密码,修改代码如下:
@Provides @Singleton fun providePMDatabase(app: MyApplication): MyAppDatabase { // 直接获取密钥二进制字节作为密码,不需要转字符串 val passphrase = generateSymmetricKey("mykey").encoded val factory = SupportFactory(passphrase, null, false) return Room .databaseBuilder(app, MyAppDatabase::class.java, BuildConfig.DATABASE_NAME) .openHelperFactory(factory) .fallbackToDestructiveMigration() .build() } // 原方法仅调整返回值类型即可 private fun generateSymmetricKey(keyAlias: String): SecretKey { val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(null) if (!keyStore.containsAlias(keyAlias)) { val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") val keyGenParameterSpec = KeyGenParameterSpec.Builder( keyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) // Android 12+ 可选添加,避免密钥备份还原后无法使用 .setUserAuthenticationRequired(false) .build() keyGenerator.init(keyGenParameterSpec) return keyGenerator.generateKey() as SecretKey } return keyStore.getKey(keyAlias, null) as SecretKey }
2. 升级依赖版本(可选,推荐)
升级Room和相关依赖到适配Android 12的稳定版,规避已知兼容问题:
//Room implementation "androidx.room:room-runtime:2.5.0" implementation "androidx.room:room-rxjava2:2.5.0" kapt "androidx.room:room-compiler:2.5.0" implementation 'net.zetetic:android-database-sqlcipher:4.5.4@aar' implementation "androidx.sqlite:sqlite-ktx:2.3.1"
3. 数据兼容处理
如果需要保留已有用户的数据,需要先捕获解密失败的错误,用旧的密码逻辑尝试解密数据库,再用新的密码重新加密;如果允许清空旧数据,直接卸载重装应用即可让新逻辑生效。
内容的提问来源于stack exchange,提问作者sb_269
相关产品推荐
相关产品推荐

