Platform-Specific Secure Storage
Platform-Specific Secure Storage
Each platform provides specialized secure storage mechanisms that leverage hardware security features.
iOS Keychain Services Deep Dive:
// Advanced Keychain implementation with access control
import Security
import LocalAuthentication
class KeychainService {
enum KeychainError: Error {
case duplicateItem
case itemNotFound
case invalidData
case authenticationFailed
case unexpectedError(OSStatus)
}
// Save data with biometric protection
func saveSensitiveData(
_ data: Data,
service: String,
account: String,
requiresBiometric: Bool = true
) throws {
var query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecValueData as String: data
]
if requiresBiometric {
var error: Unmanaged<CFError>?
let accessControl = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.biometryCurrentSet],
&error
)
if let accessControl = accessControl {
query[kSecAttrAccessControl as String] = accessControl
} else if let error = error {
throw error.takeRetainedValue() as Error
}
} else {
query[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
}
// Delete any existing item
SecItemDelete(query as CFDictionary)
// Add new item
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedError(status)
}
}
// Retrieve data with authentication
func retrieveSensitiveData(
service: String,
account: String,
authenticationPrompt: String
) throws -> Data {
let context = LAContext()
context.localizedReason = authenticationPrompt
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecUseAuthenticationContext as String: context
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess else {
if status == errSecItemNotFound {
throw KeychainError.itemNotFound
} else {
throw KeychainError.unexpectedError(status)
}
}
guard let data = result as? Data else {
throw KeychainError.invalidData
}
return data
}
}
Android Encrypted SharedPreferences and DataStore:
// Modern secure storage with DataStore
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import androidx.security.crypto.EncryptedFile
import androidx.security.crypto.MasterKeys
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import java.io.File
class SecureDataStore(private val context: Context) {
// Create encrypted DataStore
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "secure_prefs",
produceMigrations = { context ->
listOf(
EncryptedSharedPreferencesMigration(context)
)
}
)
// Define keys
companion object {
val USER_TOKEN = stringPreferencesKey("user_token")
val USER_ID = intPreferencesKey("user_id")
val BIOMETRIC_ENABLED = booleanPreferencesKey("biometric_enabled")
}
// Save encrypted data
suspend fun saveUserToken(token: String) {
context.dataStore.edit { preferences ->
preferences[USER_TOKEN] = token
}
}
// Retrieve encrypted data
val userToken: Flow<String?> = context.dataStore.data
.map { preferences ->
preferences[USER_TOKEN]
}
// Encrypted file operations
fun saveSecureFile(filename: String, content: ByteArray) {
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val file = File(context.filesDir, filename)
val encryptedFile = EncryptedFile.Builder(
file,
context,
masterKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().use { output ->
output.write(content)
}
}
fun readSecureFile(filename: String): ByteArray {
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val file = File(context.filesDir, filename)
val encryptedFile = EncryptedFile.Builder(
file,
context,
masterKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
return encryptedFile.openFileInput().use { input ->
input.readBytes()
}
}
}