如何使用Android KeyStore的SecretKey配合SQLCipher加密SQLite数据库
解决方案
首先明确:你当前生成的AndroidKeyStore密钥无法直接提取原始字节转CharArray,因为你没有配置密钥可导出权限,AndroidKeyStore默认会将密钥存储在TEE/SE安全硬件中,禁止导出原始密钥材料,调用secretKey.encoded会直接返回null。
可行方案1:修改密钥配置允许导出,直接转换
如果可以接受密钥可导出,修改密钥生成代码,添加可导出配置:
private val keyGenParameterSpec = KeyGenParameterSpec.Builder( KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT ) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) // 添加这行,允许导出密钥原始材料 .setKeyIsExportable(true) .build()
注意:如果之前已经生成过同别名的密钥,需要先删除旧密钥再重新生成,否则配置不生效:
val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(null) keyStore.deleteEntry(KEY_ALIAS)
拿到SecretKey后,将密钥字节转十六进制字符串再转CharArray即可(直接转String可能因为不可见字符出现编码问题,十六进制更稳妥):
fun getSqlCipherKey(): CharArray? { val secretKey = getKey() // 导出密钥原始字节 val keyBytes = secretKey.encoded ?: return null // 字节转十六进制字符串 val hexKey = keyBytes.joinToString("") { byte -> "%02x".format(byte) } return hexKey.toCharArray() }
用完CharArray后建议手动清空内存,避免敏感信息残留:
import java.util.Arrays // 使用后清空 Arrays.fill(charArray, ' ')
可行方案2:密钥不导出,更高安全等级
如果不希望密钥可导出,可以调整逻辑:
- 随机生成一个符合SQLCipher要求的CharArray密码
- 用当前AndroidKeyStore存储的SecretKey加密这个密码,把加密后的密文存在SharedPreferences等本地存储
- 需要使用SQLCipher时,用SecretKey解密密文得到CharArray密码即可
这种方案下密钥全程不会离开AndroidKeyStore安全区域,安全等级更高。
内容的提问来源于stack exchange,提问作者Mox4
相关产品推荐
相关产品推荐

