Secure Storage
Secure storage is a fundamental security requirement for mobile applications handling sensitive data. This includes user credentials, API tokens, personal information, and financial data. Both Android and iOS provide platform-specific secure storage mechanisms that leverage hardware security features when available.
Android Secure Storage
EncryptedSharedPreferences
Android's EncryptedSharedPreferences
provides automatic encryption/decryption of stored data using AES encryption with hardware-backed keys when available.
// Create MasterKey for encryption
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
// Create EncryptedSharedPreferences
val encryptedPrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// Store sensitive data
encryptedPrefs.edit()
.putString("auth_token", authToken)
.putString("user_credentials", credentials)
.apply()
Android Keystore System
The Android Keystore system provides hardware-backed key storage for cryptographic operations.
// Generate keys in Android Keystore
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
"secure_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(300)
.build()
keyGenerator.init(keyGenParameterSpec)
val secretKey = keyGenerator.generateKey()
// Use key for encryption
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encryptedData = cipher.doFinal(plainTextData.toByteArray())
Key Management Features
Master Key Creation and Management
class SecureStorageManager {
private val masterKey: MasterKey by lazy {
MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.setRequestStrongBoxBacked(true) // Use StrongBox if available
.build()
}
fun createEncryptedPreferences(fileName: String): SharedPreferences {
return EncryptedSharedPreferences.create(
context,
fileName,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
}
Key Rotation Support
class KeyRotationManager {
fun rotateKeys() {
val oldPrefs = getEncryptedPreferences("old_key")
val newPrefs = createNewEncryptedPreferences("new_key")
// Migrate data to new encrypted storage
val allData = oldPrefs.all
newPrefs.edit().apply {
allData.forEach { (key, value) ->
when (value) {
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
is Int -> putInt(key, value)
// Handle other types
}
}
}.apply()
// Clear old storage
oldPrefs.edit().clear().apply()
}
}
iOS Secure Storage
Keychain Services
iOS Keychain provides secure storage for sensitive information with optional iCloud synchronization and biometric protection.
import Security
import LocalAuthentication
class KeychainManager {
func store(data: Data, forKey key: String, requireBiometry: Bool = false) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
var finalQuery = query
if requireBiometry {
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryAny,
nil
)
finalQuery[kSecAttrAccessControl as String] = access
}
// Delete existing item
SecItemDelete(query as CFDictionary)
// Add new item
let status = SecItemAdd(finalQuery as CFDictionary, nil)
return status == errSecSuccess
}
func retrieve(forKey key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
return status == errSecSuccess ? result as? Data : nil
}
}
Keychain Access Groups
Enable secure data sharing between apps from the same developer:
class SharedKeychainManager {
private let accessGroup = "group.com.company.shared"
func storeSharedData(data: Data, forKey key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessGroup as String: accessGroup,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
}
iCloud Keychain Synchronization
func storeWithiCloudSync(data: Data, forKey key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrSynchronizable as String: true, // Enable iCloud sync
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
Cross-Platform Secure Storage
React Native Implementation
import Keychain from 'react-native-keychain';
import EncryptedStorage from 'react-native-encrypted-storage';
class SecureStorageService {
// Store credentials with biometric protection
async storeCredentials(username: string, password: string): Promise<boolean> {
try {
await Keychain.setCredentials(username, password, {
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
authenticatePrompt: 'Authenticate to access your credentials',
service: 'MyAppService',
storage: Keychain.STORAGE_TYPE.KC, // Keychain on iOS, Keystore on Android
});
return true;
} catch (error) {
console.error('Failed to store credentials:', error);
return false;
}
}
// Store general secure data
async storeSecureData(key: string, value: string): Promise<void> {
try {
await EncryptedStorage.setItem(key, value);
} catch (error) {
console.error('Failed to store secure data:', error);
throw error;
}
}
async getSecureData(key: string): Promise<string | null> {
try {
return await EncryptedStorage.getItem(key);
} catch (error) {
console.error('Failed to retrieve secure data:', error);
return null;
}
}
}
Flutter Implementation
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:local_auth/local_auth.dart';
class SecureStorageService {
static const _storage = FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
keyCipherAlgorithm: KeyCipherAlgorithm.RSA_ECB_PKCS1Padding,
storageCipherAlgorithm: StorageCipherAlgorithm.AES_GCM_NoPadding,
),
iOptions: IOSOptions(
accessibility: IOSAccessibility.first_unlock_this_device,
),
);
Future<void> storeSecureData(String key, String value, {bool requireBiometry = false}) async {
Map<String, String> options = {};
if (requireBiometry) {
final localAuth = LocalAuthentication();
final isAuthenticated = await localAuth.authenticate(
localizedReason: 'Authenticate to store secure data',
options: const AuthenticationOptions(
biometricOnly: true,
stickyAuth: true,
),
);
if (!isAuthenticated) {
throw Exception('Biometric authentication failed');
}
}
await _storage.write(key: key, value: value, aOptions: const AndroidOptions(
requireAuthentication: true,
));
}
Future<String?> getSecureData(String key) async {
try {
return await _storage.read(key: key);
} catch (e) {
print('Error reading secure data: $e');
return null;
}
}
}
Best Practices for Secure Storage
Data Classification
- Public Data: No encryption required
- Internal Data: Basic encryption
- Confidential Data: Strong encryption + access controls
- Restricted Data: Hardware-backed encryption + biometry
Key Management Strategy
// Android key management example
class KeyManagementStrategy {
companion object {
const val KEY_ALIAS_AUTH_TOKEN = "auth_token_key"
const val KEY_ALIAS_USER_DATA = "user_data_key"
const val KEY_ALIAS_FINANCIAL = "financial_key"
}
fun getKeyForDataType(dataType: DataType): String {
return when (dataType) {
DataType.AUTH_TOKEN -> KEY_ALIAS_AUTH_TOKEN
DataType.USER_DATA -> KEY_ALIAS_USER_DATA
DataType.FINANCIAL -> KEY_ALIAS_FINANCIAL
}
}
fun createKeyWithBiometricProtection(alias: String) {
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true) // Key invalidated if biometry changes
.build()
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
}
}
Error Handling and Fallbacks
class RobustSecureStorage {
enum StorageError: Error {
case biometryNotAvailable
case biometryNotEnrolled
case biometryLockout
case keyNotFound
case decryptionFailed
}
func secureStore(data: Data, key: String) async throws {
do {
// Try biometric-protected storage first
try await storeBiometricProtected(data: data, key: key)
} catch StorageError.biometryNotAvailable {
// Fallback to passcode-protected storage
try await storePasscodeProtected(data: data, key: key)
} catch StorageError.biometryNotEnrolled {
// Prompt user to enroll biometry or use passcode
try await handleBiometryEnrollment(data: data, key: key)
}
}
private func handleBiometryEnrollment(data: Data, key: String) async throws {
// Implementation for handling biometry enrollment
}
}
Security Considerations
Key Rotation
- Implement automatic key rotation for long-lived applications
- Provide migration paths for existing encrypted data
- Monitor key usage and lifecycle
Backup and Recovery
- Design secure backup strategies for encrypted data
- Implement key escrow for enterprise environments
- Plan for device migration scenarios
Compliance Requirements
- GDPR: Right to be forgotten implementation
- PCI DSS: Secure payment data storage
- HIPAA: Healthcare data protection
- SOX: Financial data integrity
Secure storage is the foundation of mobile application security. Proper implementation using platform-specific APIs, combined with sound key management practices, ensures that sensitive user data remains protected even if the device is compromised.