Every app handles data in two states: in transit (moving over the network) and at rest (stored on the device). Each state has different threats and requires different defenses.
Data in Transit: TLS
All network traffic must use TLS. Android enforces this via the Network Security Configuration:
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<!-- Block all cleartext traffic (enforces HTTPS everywhere) -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<!-- Trust only the system CA store — no user-installed CAs -->
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
<!-- AndroidManifest.xml -->
<application
android:networkSecurityConfig="@xml/network_security_config"
... />
cleartextTrafficPermitted="false" causes any HTTP (non-TLS) request to throw a java.io.IOException. This is the default on API 28+, but making it explicit is good practice.
Certificate Pinning (In Transit)
For high-security endpoints, pin the certificate hash so the app rejects unexpected certificates:
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") // leaf
.add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
Get the pin hash:
# From the certificate file
openssl x509 -in cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
Certificate pinning risks:
- If the pin expires without an app update → all users are blocked
- Pin at the intermediate CA level, not the leaf, to reduce rotation risk
- Always include a backup pin
Data at Rest: File Encryption
Use EncryptedFile for files that contain sensitive data:
// implementation("androidx.security:security-crypto:1.1.0-alpha06")
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedFile = EncryptedFile.Builder(
context,
File(context.filesDir, "sensitive_data.enc"),
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
// Write
encryptedFile.openFileOutput().use { output ->
output.write(sensitiveData.toByteArray())
}
// Read
encryptedFile.openFileInput().use { input ->
val content = input.readBytes().toString(Charsets.UTF_8)
}
Data at Rest: Database Encryption (SQLCipher)
For full database encryption, use SQLCipher — a Room-compatible SQLite variant that encrypts the entire database file:
// implementation("net.zetetic:android-database-sqlcipher:4.5.4")
// implementation("androidx.sqlite:sqlite-ktx:2.3.1")
val passphrase = SQLiteDatabase.getBytes(userPassword.toCharArray())
val factory = SupportFactory(passphrase)
val db = Room.databaseBuilder(context, AppDatabase::class.java, "encrypted.db")
.openHelperFactory(factory) // ← uses SQLCipher instead of standard SQLite
.build()
Passphrase management:
- Don't hardcode the passphrase — generate it on first run and store in Keystore-encrypted preferences
- Or derive from user's biometric/PIN-gated Keystore key — requires authentication before any DB access
What NOT to Encrypt
Over-encryption has costs — increased complexity, performance overhead, and key management burden:
| Data | Encrypt at Rest? |
|---|---|
| Auth tokens, session keys | Yes — EncryptedSharedPreferences |
| Private messages, health data | Yes — EncryptedFile or SQLCipher |
| User name, profile photo URL | No — public data, low sensitivity |
| App config, feature flags | No — not sensitive |
| Cached API responses | Situational — if they contain PII |
Key Takeaways
| Concern | Solution |
|---|---|
| All network traffic | cleartextTrafficPermitted="false" in Network Security Config |
| High-security API endpoints | Certificate pinning with OkHttp CertificatePinner |
| Sensitive preference values | EncryptedSharedPreferences (Jetpack Security) |
| Sensitive files | EncryptedFile (Jetpack Security) |
| Full database encryption | SQLCipher with Room |
| Passphrase storage | Generate once, encrypt with Keystore |