第17章 安全与加密技术¶
学习目标:掌握Android安全开发,理解加密算法、安全存储、代码混淆等技术。
预计学习时间:3-5天 实践时间:1-2天
目录¶
1. 安全概述¶
1.1 Android安全模型¶
- 应用沙箱:每个应用运行在独立的进程中
- 权限系统:访问敏感资源需要权限
- 签名机制:应用发布需要签名
- 加密存储:敏感数据加密保存
1.2 常见安全威胁¶
- 数据泄露
- 中间人攻击
- 代码注入
- 逆向工程
2. 数据加密¶
2.1 对称加密¶
Kotlin
// AES 对称加密工具类
object AESUtil {
private const val ALGORITHM = "AES" // 加密算法名称
private const val TRANSFORMATION = "AES/CBC/PKCS7Padding" // 算法/模式/填充方式
private const val IV_SIZE = 16 // 初始化向量长度(CBC 模式必需)
private const val KEY_SIZE = 32 // 密钥长度 32 字节 = AES-256
/**
* 从密码派生固定长度密钥(生产环境推荐使用 PBKDF2)
*/
private fun deriveKey(password: String): SecretKeySpec {
val keyBytes = password.toByteArray(Charsets.UTF_8)
.copyOf(KEY_SIZE) // 截断或零填充到 32 字节,保证密钥长度固定
return SecretKeySpec(keyBytes, ALGORITHM)
}
// 加密函数:明文 + 密码 → Base64 编码的密文
fun encrypt(data: String, password: String): String {
val cipher = Cipher.getInstance(TRANSFORMATION) // 获取 AES/CBC 加密器实例
val secretKey = deriveKey(password) // 从密码派生密钥
// 生成随机 IV(每次加密使用不同 IV,增强安全性)
val iv = ByteArray(IV_SIZE).also { SecureRandom().nextBytes(it) }
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
val encrypted = cipher.doFinal(data.toByteArray()) // 执行加密
// 将 IV 与密文拼接后 Base64 编码,解密时需要同一 IV
return Base64.encodeToString(iv + encrypted, Base64.DEFAULT)
}
// 解密函数:Base64 密文 + 密码 → 明文字符串
fun decrypt(encryptedData: String, password: String): String {
val combined = Base64.decode(encryptedData, Base64.DEFAULT) // Base64 解码
val iv = combined.copyOfRange(0, IV_SIZE) // 提取前 16 字节作为 IV
val cipherText = combined.copyOfRange(IV_SIZE, combined.size) // 剩余部分为密文
val cipher = Cipher.getInstance(TRANSFORMATION)
val secretKey = deriveKey(password)
cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv)) // 使用相同 IV 初始化
val decrypted = cipher.doFinal(cipherText) // 执行解密
return String(decrypted) // 字节数组转字符串
}
}
2.2 非对称加密¶
Kotlin
// RSA 非对称加密工具类(公钥加密、私钥解密)
object RSAUtil {
// 生成 RSA 密钥对(包含公钥和私钥)
fun generateKeyPair(): KeyPair {
val keyGen = KeyPairGenerator.getInstance("RSA") // 获取 RSA 密钥生成器
keyGen.initialize(2048) // 设置密钥长度为 2048 位(安全推荐值)
return keyGen.generateKeyPair() // 返回生成的公钥/私钥对
}
// 使用公钥加密数据,返回 Base64 编码的密文
fun encrypt(data: String, publicKey: PublicKey): String {
val cipher = Cipher.getInstance("RSA") // 获取 RSA 加密器
cipher.init(Cipher.ENCRYPT_MODE, publicKey) // 用公钥初始化为加密模式
// 加密字节数据并进行 Base64 编码,便于传输和存储
return Base64.encodeToString(cipher.doFinal(data.toByteArray()), Base64.DEFAULT)
}
}
3. 安全存储¶
3.1 EncryptedSharedPreferences¶
Kotlin
// 创建主密钥,使用 AES256-GCM 方案(密钥存储在 Android Keystore 中)
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
// 创建加密的 SharedPreferences 实例
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs", // 加密存储文件名
masterKey, // 用于加解密的主密钥
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, // 键名加密方案
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM // 键值加密方案
)
// 像普通 SharedPreferences 一样使用,数据会自动加密存储
sharedPreferences.edit().putString("api_key", "secret_key").apply()
3.2 Keystore系统¶
Kotlin
// 在 Android Keystore 中生成 AES 密钥(密钥由硬件安全模块保护,无法导出)
val keyGenerator = KeyGenerator.getInstance("AES", "AndroidKeyStore")
// 配置密钥参数
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
"my_key", // 密钥别名,后续通过此名称引用密钥
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT // 密钥用途:加密和解密
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM) // 使用 GCM 模式(支持认证加密)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) // GCM 模式不需要填充
.build()
keyGenerator.init(keyGenParameterSpec) // 用参数初始化密钥生成器
keyGenerator.generateKey() // 生成密钥并自动存储到 Keystore
4. 代码安全¶
4.1 ProGuard/R8混淆¶
Text Only
# 保留指定类名不被混淆(第三方库或反射使用的类需要保留)
-keep public class com.example.MyClass
# 保留被 Gson @SerializedName 注解标记的字段,防止 JSON 序列化/反序列化失败
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# 混淆后保留源文件名和行号,便于线上崩溃日志的堆栈追踪
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
4.2 根检测¶
Kotlin
// 检测设备是否已 Root(Root 设备存在安全风险,可能被恶意利用)
fun isDeviceRooted(): Boolean {
// 常见的 Root 标志文件路径列表
val paths = arrayOf(
"/system/app/Superuser.apk", // SuperUser 管理应用
"/sbin/su", // su 二进制文件(常见路径)
"/system/bin/su",
"/system/xbin/su"
)
// 只要任意一个路径存在,就判定设备已 Root
return paths.any { File(it).exists() }
}
5. 网络安全¶
5.1 SSL Pinning¶
Kotlin
// SSL 证书固定(Certificate Pinning)——防止中间人攻击
// 将服务器证书的 SHA-256 指纹绑定到客户端,只信任指定证书
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") // 域名 + 证书哈希
.build()
// 创建配置了证书固定的 OkHttp 客户端
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner) // 启用证书固定校验
.build()
5.2 安全网络配置¶
XML
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">sha256_hash_here</pin>
</pin-set>
</domain-config>
</network-security-config>
6. 实践练习¶
练习1:加密存储用户数据¶
任务:实现用户敏感信息加密存储
要求: - 使用EncryptedSharedPreferences - 密钥存储在Keystore - 支持数据读取
练习2:安全网络请求¶
任务:实现证书固定的网络请求
要求: - 配置SSL Pinning - 处理证书错误 - 日志记录
本章小结¶
核心要点¶
- 加密算法:AES对称加密,RSA非对称加密
- 安全存储:EncryptedSharedPreferences,Keystore
- 代码安全:ProGuard混淆,根检测
- 网络安全:SSL Pinning,HTTPS
下一步¶
完成本章学习后,请进入第18章:音视频开发。
本章完成时间:预计3-5天