Android 安全模型

Android 的安全靠三层:应用沙箱隔离进程、权限体系控制能力、加密保护数据。前两层是系统给的,第三层要开发者自己做好。

权限管的是谁能进来,加密管的是拿走了也读不懂。

常见误区:把 token 明文存 SharedPreferences、日志里打密码、忽略证书校验。安全是系统工程,先从最容易被翻出来的数据入手。

Keystore 与密钥管理

Android Keystore 把密钥放在系统级保险箱里,App 只能使用不能导出。用它生成 AES 密钥,再配合设备锁屏解锁,安全性直接拉满。

val generator = KeyGenerator.getInstance(
  KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
generator.init(KeyGenParameterSpec.Builder(
  "app_key",
  KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
  .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
  .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
  .build())
val key = generator.generateKey()
Keystore 特性
  • 硬件隔离密钥不出安全芯片。
  • 不可导出拿不到密钥原文。
  • 解锁绑定可要求设备解锁可用。

密钥别名用固定字符串,别把密钥对象序列化存文件。Keystore 密钥删了就没了,迁移要设计好再删。

AES/GCM 数据加密

AES-GCM 是推荐的对称加密:一次加密同时保证机密和完整性。每次加密生成随机 IV,IV 与密文一起存,解密时取回来。

fun encrypt(key: SecretKey, plain: ByteArray): ByteArray {
  val cipher = Cipher.getInstance("AES/GCM/NoPadding")
  cipher.init(Cipher.ENCRYPT_MODE, key)
  val iv = cipher.iv
  val encrypted = cipher.doFinal(plain)
  return iv + encrypted // IV 拼在密文前面
}

fun decrypt(key: SecretKey, data: ByteArray): ByteArray {
  val iv = data.copyOfRange(0, 12)
  val body = data.copyOfRange(12, data.size)
  val cipher = Cipher.getInstance("AES/GCM/NoPadding")
  cipher.init(Cipher.DECRYPT_MODE, key, GCMParameterSpec(128, iv))
  return cipher.doFinal(body)
}
Android 安全:中央发光盾牌带锁孔、两侧钥匙与加密数据卡片节点由连接线相连的蓝调插画
盾牌守护加密数据。

IV 每次必须不同,否则相同明文会出相同密文,泄露规律。别自己发明加密算法,用标准库的 AES/GCM 就够安全。

安全存储与偏好

Android 6 之后有 EncryptedSharedPreferences,键值整体加密;Jetpack Security 库的 EncryptedFile 加密整个文件。token、密钥、敏感配置都放这里。

val masterKey = MasterKey.Builder(context)
  .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
  .build()

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

加密存储不是万能:根目录下攻破的设备任何保护都有限。但加密能挡住常规的数据窃取和备份泄露,成本低收益高,默认就该用。

密钥与数据分开存放:加密后的密文可以进 SharedPreferences 或数据库,但密钥永远留在 Keystore。每次加密都生成随机 IV 并随密文保存,解密时取回;验证完整性用 GCM 的校验能力,解密失败说明数据被篡改或密钥不匹配,此时应当清空本地敏感数据并引导用户重新登录,而不是把错误数据继续用下去,同时把异常上报到监控,观察是否有人批量触发解密失败。备份恢复也要考虑:Keystore 密钥随设备走,换机时通过加密迁移或重新登录来重建密钥,别把密文拷过去却丢了钥匙。

还有一点:加密不是越复杂越好,标准库的 AES/GCM 足够;把算法参数写死在代码里,避免运行时配置被攻击者改写。