- 传输安全:确保数据在客户端和服务器之间传输时不会被窃听或篡改,这是最基本也是最重要的安全措施。
- 数据加密:在客户端将敏感数据(如密码、Token)进行加密后再发送,或者在服务器端对存储的敏感数据进行加密。
下面我将详细讲解这两个层面,并提供具体的实现方案和最佳实践。

传输安全 - HTTPS
这是必须要做的,是所有网络请求安全的基础,HTTPS = HTTP + SSL/TLS。
为什么必须使用 HTTPS?
- 机密性:通过 SSL/TLS 加密,中间人(如黑客、运营商)无法窃听你传输的内容。
- 完整性:通过消息认证码(MAC)确保数据在传输过程中没有被篡改。
- 身份认证:通过服务器证书验证服务器的真实身份,防止你连接到了一个假冒的“钓鱼”服务器。
如何在 Android 中实现 HTTPS?
现代 Android 开发中,使用 HTTPS 非常简单,主要有两种方式:
使用现代网络库(推荐)
像 OkHttp、Retrofit 这样的现代网络库,默认就支持 HTTPS,并且对 Android 系统的信任库(TrustStore)有很好的兼容性,你只需要确保你的 URL 是 https:// 开头的即可。

// 使用 OkHttp 的示例
val client = OkHttpClient.Builder()
.build()
val request = Request.Builder()
.url("https://api.example.com/data")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 处理失败
}
override fun onResponse(call: Call, response: Response) {
// 处理成功
response.body?.string()
}
})
处理自签名证书或不受信任的证书(特殊情况)
在某些开发或测试环境中,服务器可能使用自签名证书,或者证书不受 Android 系统默认信任,这时,直接请求会抛出 SSLHandshakeException。
⚠️ 安全警告: 不要在生产环境中绕过证书验证! 这会使你的应用完全暴露在中间人攻击之下,此方法仅适用于受控的测试环境。
如果必须处理,可以这样配置 OkHttp:

// 创建一个信任所有证书的 TrustManager
val trustAllCerts = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
})
// 安装这个信任管理器
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sslContext.socketFactory
// 配置 OkHttp
val client = OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
// 禁用主机名验证(同样危险!)
.hostnameVerifier { _, _ -> true }
.build()
正确的做法是让服务器管理员将证书安装到所有 Android 设备的信任存储中,或者让服务器使用由公共 CA(如 Let's Encrypt, DigiCert)签名的证书。
数据加密
当数据本身非常敏感时(如用户密码、支付信息),即使使用了 HTTPS,有时我们仍然希望在应用层对其进行额外的加密,这通常被称为“端到端加密”。
常见加密场景和方案
场景 1:用户密码
- 最佳实践:永远不要在前端对密码进行可逆加密!
- 正确做法:用户注册时,在客户端直接明文将密码发送到服务器,服务器接收到密码后,使用单向哈希算法(如
SHA-256)+ 盐 进行处理,然后将哈希值存储在数据库中。 - 为什么? 因为单向哈希是不可逆的,即使数据库泄露,攻击者也无法直接得到原始密码,加盐是为了防止彩虹表攻击。
- 登录流程:客户端将明文密码发送 -> 服务器用同样的哈希算法处理 -> 与数据库中存储的哈希值比对。
场景 2:敏感请求参数(如手机号、身份证号)
API 的某个参数包含敏感信息,而你又希望即使服务器的日志被泄露,这些信息也是不可读的,那么可以在客户端进行加密。
步骤:
- 客户端加密:在发送请求前,使用约定的加密算法对敏感数据进行加密。
- 服务器解密:服务器接收到请求后,使用对应的解密算法进行解密,然后处理业务逻辑。
常用加密算法:
- AES (Advanced Encryption Standard):对称加密算法,加密和解密使用同一个密钥,速度快,适合大量数据加密。
- RSA:非对称加密算法,使用公钥加密,私钥解密,密钥分发方便,但速度较慢,通常用于加密 AES 的密钥(密钥交换)。
示例:使用 AES 加密敏感数据
假设我们要加密一个手机号。
添加依赖
在 app/build.gradle 中添加:
implementation 'org.bouncycastle:bcprov-jdk15on:1.70' // 一个强大的加密库
加密工具类
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
import java.util.Base64
object AESUtils {
// AES 加密
fun encrypt(data: String, secretKey: String): String {
// 密钥必须是 16, 24, 或 32 字节长,对应 AES-128, AES-192, or AES-256
val key = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), "AES")
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding") // 选择加密模式和填充
cipher.init(Cipher.ENCRYPT_MODE, key)
val encryptedBytes = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
return Base64.getEncoder().encodeToString(encryptedBytes)
}
// AES 解密
fun decrypt(encryptedData: String, secretKey: String): String {
val key = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), "AES")
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, key)
val decodedBytes = Base64.getDecoder().decode(encryptedData)
val decryptedBytes = cipher.doFinal(decodedBytes)
return String(decryptedBytes, Charsets.UTF_8)
}
}
在网络请求中使用
val phoneNumber = "13800138000"
val secretKey = "ThisIsASecretKey123" // 这个密钥必须与服务器约定好!
// 加密
val encryptedPhone = AESUtils.encrypt(phoneNumber, secretKey)
println("加密后的手机号: $encryptedPhone")
// 使用 Retrofit 发送请求
// @Field("phone") encryptedPhone
服务器端需要有对应的 AES 解密代码,使用相同的 secretKey 来解密 encryptedPhone。
- 强制 HTTPS:所有生产环境的网络请求必须使用 HTTPS,这是底线。
- 使用现代网络库:优先选择 OkHttp 和 Retrofit,它们已经处理了大部分 SSL/TLS 的复杂性。
- 不要在前端加密密码:让服务器使用
SHA-256+ 盐来处理密码哈希。 - 敏感数据端到端加密(可选):对于非密码但极其敏感的数据(如金融信息),可以在客户端和服务器端约定使用 AES 进行加密/解密。
- 密钥管理是关键:客户端的加密密钥(
secretKey)如何安全地存储和管理是一个难题,硬编码在代码中是不安全的,可以考虑使用 Android 的Keystore系统,但实现复杂,这个密钥可以通过服务器下发,或者与用户登录态绑定。
- 密钥管理是关键:客户端的加密密钥(
- 混淆代码:使用 ProGuard 或 R8 混淆你的代码,增加逆向工程的难度,保护你的加密逻辑和密钥不被轻易发现。
- 安全审计:定期对你的应用进行安全审计,检查是否存在已知漏洞。
完整流程示例(Retrofit + HTTPS + AES 加密)
目标:向服务器提交一个包含加密手机号的请求。
定义 API 接口
interface ApiService {
@FormUrlEncoded
@POST("user/register")
suspend fun register(
@Field("username") username: String,
@Field("phone") phone: String // 这里将传入加密后的手机号
): Response<RegisterResponse>
}
创建 Retrofit 实例
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/") 