数据存储安全:SharedPreferences加密、Room数据库加密、KeyStore使用、文件加密存储

数据存储这块,说实话是很多安全漏洞的重灾区。我见过太多应用直接把用户密码、Token明文扔进SharedPreferences,简直就像把家门钥匙挂在门外。今天咱们就聊聊怎么把这些存储手段武装起来。

SharedPreferences加密:别让明文裸奔

SharedPreferences用起来方便,但默认是明文存储的。你想想看,只要手机被root,或者有人能读到你的应用数据目录,所有信息一览无余。

⚠️ 注意:不要自己写加密工具类!我见过有人用固定密钥硬编码在代码里,这跟没加密差不多。

我个人习惯用 EncryptedSharedPreferences,这是Android官方提供的方案。它背后用的是AES-256加密,密钥由Android Keystore管理。

// 初始化加密的SharedPreferences
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

// 使用方式跟普通SharedPreferences一样
sharedPreferences.edit().putString("token", "your_token").apply()
val token = sharedPreferences.getString("token", null)

这里有个坑——PrefKeyEncryptionSchemePrefValueEncryptionScheme要选对。我刚开始用的时候选错了,结果key和value都用了同样的加密模式,后来发现key的加密应该用AES256_SIV,value用AES256_GCM。为什么?因为key需要确定性加密,方便查找;value需要随机化加密,更安全。

Room数据库加密:SQLCipher来帮忙

Room本身不提供加密功能,但我们可以用SQLCipher。说白了,SQLCipher就是给SQLite加了一层透明的加密解密层。

我在项目中遇到过一个问题:用户数据量很大,加密后性能下降明显。后来发现是没配置好加密参数。

// 在Room中集成SQLCipher
val passphrase = "your_secure_passphrase" // 建议从KeyStore获取

val database = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "encrypted_db"
)
    .openHelperFactory(SupportFactory(passphrase))
    .build()
💡 小技巧:密码不要硬编码!从KeyStore获取,或者用用户输入的PIN码经过PBKDF2派生。

性能优化方面,我建议:

  • 设置合适的页面大小(默认4096字节,可以试试1024)
  • 使用WAL模式提升并发性能
  • 定期执行PRAGMA optimize

KeyStore使用:密钥的保险箱

Android KeyStore是什么?它是系统级的硬件安全模块。密钥一旦存入,就永远无法以明文形式导出。嗯,这里要注意:KeyStore只负责存密钥,不负责加密数据

我曾经犯过一个错误:把密钥直接存在SharedPreferences里,结果被逆向工具轻松提取。后来改用KeyStore,才真正解决了密钥泄露问题。

// 生成并存储密钥到KeyStore
val keyGenerator = KeyGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_AES,
    "AndroidKeyStore"
)

val keyGenSpec = KeyGenParameterSpec.Builder(
    "my_key_alias",
    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
    .setKeySize(256)
    .build()

keyGenerator.init(keyGenSpec)
keyGenerator.generateKey()

// 使用密钥进行加密
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val secretKey = keyStore.getKey("my_key_alias", null) as SecretKey

val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.iv // 保存IV,解密时需要
val encryptedData = cipher.doFinal(plaintext)
🔑 关键点:KeyStore支持生物识别认证。你可以设置只有通过指纹或人脸识别才能使用密钥,这样即使手机丢了,别人也解不开你的数据。

文件加密存储:大文件的保护方案

对于图片、视频这类大文件,SharedPreferences和Room都不合适。我们需要直接对文件进行加密。

我推荐使用 Android File Encryption 或者自己封装AES-GCM。为什么选GCM?因为它同时提供加密和完整性校验,能防止数据被篡改。

// 文件加密示例(使用AES-GCM)
fun encryptFile(inputFile: File, outputFile: File, secretKey: SecretKey) {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    
    // 将IV写入文件头部
    val iv = cipher.iv
    outputFile.outputStream().use { output ->
        output.write(iv)
        
        inputFile.inputStream().use { input ->
            val cipherOutputStream = CipherOutputStream(output, cipher)
            input.copyTo(cipherOutputStream)
            cipherOutputStream.close()
        }
    }
}

fun decryptFile(inputFile: File, outputFile: File, secretKey: SecretKey) {
    inputFile.inputStream().use { input ->
        // 先读取IV
        val iv = ByteArray(12) // GCM标准IV长度
        input.read(iv)
        
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        val spec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
        
        outputFile.outputStream().use { output ->
            val cipherInputStream = CipherInputStream(input, cipher)
            cipherInputStream.copyTo(output)
            cipherInputStream.close()
        }
    }
}
⚠️ 警告:文件加密后记得删除原始文件!而且要用File.delete()还不够,建议用FileOutputStream覆写随机数据后再删除,防止被恢复。

实际项目中,我通常这样组合使用:

  • 小数据(配置、Token) → EncryptedSharedPreferences
  • 结构化数据(用户信息、聊天记录) → 加密Room数据库
  • 密钥管理 → Android KeyStore
  • 大文件(图片、视频) → AES-GCM文件加密

最后说一句:加密不是万能的,但不加密是万万不能的。你想想看,用户把数据托付给你,你至少得帮人家锁好门吧?