Data at rest encryption for a mobile app means that anything the app writes to the device, whether files, databases, preferences or cached responses, is unreadable without a key the app controls. To evaluate it, ask three questions in order: which data needs it, who creates and holds the keys, and when the data must be plaintext anyway. Android and iOS already encrypt storage at the operating-system level; this article covers what app-layer encryption adds on top, where it stops, and how to review it per platform.
Start with a data inventory, not a cipher
Most encryption reviews begin with the algorithm. They should begin with a list. Every mobile app writes more than its developers expect: HTTP caches, web view storage, crash logs, keyboard caches, screenshots of the last screen and preferences written by third-party libraries.
Walk the app’s sandbox on a test device after a full user session and record every file and store. For each one, name the data, its storage boundary and the protection it needs.
| Data item | Where it lives | Sensitivity | Suggested protection |
|---|---|---|---|
| Refresh token | Keychain item or Keystore-wrapped preference | High | Platform key store |
| Signing key for API requests | Keystore or Secure Enclave, non-exportable | High | Platform key store |
| Account statements (PDF) | App documents directory | High | App-layer encryption |
| Local message database | SQLite file | High | App-layer encryption |
| User settings | Preferences or user defaults | Low | Platform storage encryption |
| Cached images | Caches directory | Low | Platform storage encryption |
| Cached API responses with personal data | HTTP cache | Medium | App-layer encryption |
| One-time passcode | Memory only | High | Do not store |
| Full card number | Not needed on device | High | Do not store |
Counts come from the example inventory table above, to show how such a table reads. Your own inventory will differ.
The last category matters most. The cheapest data to protect is data the app never writes. If an item can stay on the server, or live only in memory for one screen, remove it from the inventory before choosing a cipher.
What the platforms already give you
Both platforms encrypt user storage, but they do it differently, and the details decide what an app still has to do.
Android
- File-based encryption with credential-encrypted and device-encrypted storage
- Credential-encrypted storage is available after the user first unlocks the device
- Android Keystore keeps keys non-exportable, backed by a trusted execution environment where the device has one
- StrongBox keys live in a separate secure element on devices that include it
iOS
- Data Protection classes set per file, from Complete to None
- Default class for app files is available after the first unlock
- Keychain items carry their own accessibility class, including this-device-only variants
- Secure Enclave holds elliptic-curve keys used to sign or to wrap a small symmetric key
- 2010
iOS 4 introduces Data Protection
Files can be tied to keys derived from the device passcode.
- 2013
iOS 7 changes the default class
App files default to protection until the first user authentication; the iPhone 5s ships with the Secure Enclave.
- 2016
Android 7.0 adds file-based encryption
Direct Boot separates device-encrypted and credential-encrypted storage.
- 2018
Android 9 adds StrongBox
Keystore keys can be generated in a dedicated secure element on supported hardware.
- 2019
Android 10 and iOS 13
Devices launching with Android 10 must use file-based encryption; iOS 13 introduces CryptoKit.
Key lifecycle: generation, access, rotation, recovery
Encryption is only as strong as the place the key lives. A key written into the app package, a preference file or a string constant is readable by anyone who can read the app. The rule is simple: keys are generated on the device, inside the platform key store, and never leave it.
A common and sound pattern is envelope encryption. A non-exportable key in the platform key store wraps a data key; the data key encrypts the files. The app stores only the wrapped data key.
Generate wrapping key
Created inside Android Keystore or protected by the Secure Enclave; non-exportable.
Wrap a data key
A random symmetric data key is encrypted by the wrapping key and stored.
Encrypt the data
Files and database pages are encrypted with the data key.
Unwrap on use
The data key is unwrapped into memory when a screen needs the data.
Plaintext window
Hooking or a debugger can read the key or data at this moment.
On Android, the wrapping key is an AES key generated in Keystore. The configuration below is standard platform code:
val spec = KeyGenParameterSpec.Builder(
"local_data_wrapping_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build()
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
.apply { init(spec) }
.generateKey()
On iOS, a token belongs in the Keychain with an accessibility class that matches when the app needs it, and files get an explicit protection class:
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "refresh_token",
kSecValueData as String: tokenData,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
try statement.write(to: fileURL, options: .completeFileProtection)
Rotation and recovery
Two lifecycle events break naive designs. Rotation: when a wrapping key is replaced, the app re-wraps the data keys under the new key rather than re-encrypting every file. Recovery: this-device-only Keychain items and Keystore keys do not travel in backups or device transfers, so a restored app finds encrypted files and no key. Plan for that on day one. The right behaviour is to detect the missing key, discard the unreadable local data and fetch it again after the user signs in.
Runtime scope: where encryption stops
Encryption at rest answers one threat: someone reading storage without the app. It does not answer an attacker who runs the app, or who controls the device while the app is running.
Encryption at rest helps against
- Files copied from a lost or seized device
- Backups and device-transfer copies of app data
- Raw file reads on a rooted or jailbroken device, while keys stay in the key store
- Forensic extraction of an app's sandbox
Needs other controls
- Hooking frameworks reading data after decryption
- Debuggers and memory dumps during a session
- A repackaged app that calls the decryption code itself
- Screenshots and screen recording of decrypted content
This is where Otukan’s code and data protection fits. Otukan encrypts the files, databases and preferences your app marks as sensitive, with keys generated in Android Keystore or held in the iOS Keychain as this-device-only items. Before protected data is decrypted into memory, Otukan runs hooking and debugger checks and can refuse decryption under the policy you set. It keeps secrets out of the app package and obfuscates the code that handles keys, so the plaintext window is shorter and harder to reach. For secrets shipped with the app, see mobile app secrets protection. Platform detail is covered on Android app data encryption and iOS app data encryption.
Ask any vendor the same question: at which moment is my data plaintext, and what is watching that moment?
Evaluation checklist
- Every file and store in the sandbox is listed with its data, boundary and protection.
- Items that do not need to be stored have been removed.
- Keys are generated on the device, inside Android Keystore or protected by the Keychain and Secure Enclave, and never written to app storage.
- Android and iOS behaviour is described and tested separately, per supported OS version.
- Rotation re-wraps data keys without losing user data.
- A restore to a new device is tested, and the app recovers cleanly without the old key.
- Runtime checks run before sensitive data is decrypted.
- Any validation or certification claim comes with its certificate and scope.