Implementing Data Containerization for Corporate Mobile Apps
Data containerization on mobile—architectural principle: corporate data isolated from personal and other apps, encrypted with independent key, access protected by separate auth. Not Docker containers—mobile OS-level data container.
Platform Containerization Mechanisms
iOS: Keychain Access Groups + Data Protection API. Keychain entries with shared accessGroup accessible by multiple vendor apps—basic secret sharing. For isolation: each app has own Keychain section, inaccessible without explicit Access Group.
Data Protection classes define when data decrypts:
// File accessible only when device unlocked
let attrs: [FileAttributeKey: Any] = [
.protectionKey: FileProtectionType.complete
]
try FileManager.default.setAttributes(attrs, ofItemAtPath: filePath)
// For Keychain entries
let query: [String: Any] = [
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
kSecAttrAccessibleWhenUnlockedThisDeviceOnly—key bound to device (doesn't migrate to iCloud Backup), accessible only when screen unlocked. For corporate data—minimally sufficient protection level.
Android: Work Profile + EncryptedSharedPreferences + Keystore. Work Profile creates isolated user space with separate Keystore instance. EncryptedSharedPreferences encrypts keys and values via Tink library:
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"corporate_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
For files—EncryptedFile from androidx.security:security-crypto:
val encryptedFile = EncryptedFile.Builder(
context,
File(context.filesDir, "corporate_document.enc"),
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().use { output ->
output.write(corporateData)
}
Network-Level Isolation
Corporate data shouldn't leak through personal network channels. Implementation via per-app VPN:
-
iOS:
NEAppProxyProvider+ MDM config. MDM assigns VPN to specific bundle ID. That app's traffic goes through corporate VPN; personal apps—direct. -
Android:
VpnServicewithallowedApplicationsinVpnProfile. Only listed packages tunneled.
Without per-app VPN alternative—NSURLSession with URLSessionConfiguration.ephemeral and mandatory Corporate Proxy settings via ProxyDictionary.
Custom File Container: Own Implementation
For apps requiring full encryption control (finance, healthcare), sometimes need own file container—VeraCrypt-like vault at mobile level.
Simplified architecture:
container.vault
├── header (256 bytes): version, salt, PBKDF2 params, iv
├── index.enc: CBOR manifest (name, size, offset, per-file iv)
└── data.blob: concatenation of encrypted files (AES-256-GCM, per-file key)
Master key derived from biometrics via LAContext.evaluatePolicy + Keychain link. Without biometrics—password hash via Argon2. Container opens on auth, closes on applicationWillResignActive.
Clipboard Leak Protection
UIPasteboard.general—global, readable by any app. For corporate data use UIPasteboard.withUniqueName()—private buffer with TTL:
let privatePasteboard = UIPasteboard.withUniqueName()
privatePasteboard.setData(corporateData, forPasteboardType: UTType.plainText.identifier)
privatePasteboard.setPersistent(false)
// Auto-deleted on next launch or via TTL
On Android: ClipboardManager global until Android 10. From Android 10 apps read clipboard only in foreground—limits attack. Additionally: clear buffer on onPause if sensitive data was copied.
Implementation Stages
Analyze data and sensitivity levels → design storage schema → implement encrypted storage (Keychain + EncryptedFile) → per-app VPN config → clipboard protection → test isolation (attempt reading from other app, from backup) → security audit → deploy.
Timeline: encrypted storage with Keychain/Keystore—3–4 weeks. Full container with per-app VPN, clipboard protection, security audit—8–12 weeks. Cost is calculated individually.







