Android开发中如何安全存储加密主密钥以供SQLCipher使用?
Room + SQLCipher 场景下密钥安全存储方案
以下是Android平台下无需用户输入密钥、可自动完成密钥存取的可行方案,均可以避免硬编码密钥的安全风险:
- 首选:Jetpack Security EncryptedSharedPreferences 存储
谷歌官方推出的安全存储组件,底层依赖系统KeyStore能力,自动处理密钥加密、存储逻辑,无需手动实现复杂安全逻辑。使用时先生成256位随机字节作为SQLCipher的主密钥,直接存入EncryptedSharedPreferences即可,后续应用启动时直接读取使用。
核心初始化代码参考:
该方案兼容Android API 23及以上版本,密钥受系统KeyStore保护,搭载TEE/SE安全硬件的设备中密钥无法被导出,即使设备Root也很难拿到明文密钥。// 生成Jetpack Security的主密钥,存在系统KeyStore中 val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // 创建加密的SharedPreferences实例 val encryptedSp = EncryptedSharedPreferences.create( "secure_key_sp", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) // 存储生成的随机SQLCipher密钥,仅首次启动生成一次即可 if (encryptedSp.getString("sqlcipher_key", null) == null) { val randomKey = ByteArray(32) SecureRandom().nextBytes(randomKey) encryptedSp.edit().putString("sqlcipher_key", Base64.encodeToString(randomKey, Base64.DEFAULT)).apply() } - 次选:直接使用Android KeyStore实现
如果不想引入Jetpack Security依赖,可直接调用系统KeyStore API生成AES加密密钥,用该密钥加密你生成的SQLCipher主密钥,将加密后的密文存在普通SharedPreferences或本地文件中。使用时先从KeyStore取出AES密钥解密密文,即可得到SQLCipher的明文密钥。
该方案兼容版本和安全性和Jetpack Security方案一致,只是需要自己实现加密、解密、异常处理逻辑。 - 兜底兼容方案:密钥派生生成固定密钥
如果需要兼容Android API 23以下的老设备,可以结合应用签名哈希、ANDROID_ID等仅当前应用和设备可获取的唯一标识,通过PBKDF2、bcrypt等多轮密钥派生算法生成固定的SQLCipher密钥。注意不要直接把原始标识作为密钥使用,必须经过多轮哈希派生提升破解难度。
注意:禁止将明文密钥存入普通SharedPreferences、本地文件等可被轻易读取的位置,不要使用IMEI、MAC地址等高版本系统已限制获取的标识作为派生源。
Room对接SQLCipher时,拿到明文密钥后直接传入SupportFactory即可:
val sqlCipherKey = Base64.decode(encryptedSp.getString("sqlcipher_key", null), Base64.DEFAULT) val factory = SupportFactory(sqlCipherKey) val db = Room.databaseBuilder(context, AppDatabase::class.java, "encrypted.db") .openHelperFactory(factory) .build()
内容的提问来源于stack exchange,提问作者Moxx4
相关产品推荐
相关产品推荐

