Keychain (Sleutelbos) — een beveiligde opslag in iOS voor het veilig bewaren van wachtwoorden, cryptografische sleutels, certificaten en vertrouwelijke notities. Volgens Apple Security Documentation (2025) gebruikt Keychain hardwarematige encryptie via Secure Enclave op alle apparaten met een A7-chip en nieuwer. Inzicht in de architectuur van iOS Keychain is noodzakelijk voor elke ontwikkelaar om tokens en applicatiegeheimen correct op te slaan.
Belangrijkste
iOS Keychain — een beveiligd mechanisme voor het opslaan van vertrouwelijke gegevens, ingebouwd in het besturingssysteem van Apple. In tegenstelling tot UserDefaults of gewone bestanden, versleutelt Keychain alle elementen op hardwareniveau en biedt het fijnmazige toegangscontrole op basis van beveiligingsbeleid.
Keychain werd geïntroduceerd in iOS 2.0 en heeft sindsdien aanzienlijke veranderingen ondergaan: in iOS 7 werd ondersteuning voor hardwaresleutels via Secure Enclave toegevoegd, in iOS 9 — opsplitsing van Keychain tussen apps via Access Groups, in iOS 13 — ondersteuning voor biometrische koppeling via LAContext. Volgens Apple WWDC Session (2024) gebruikt meer dan 90% van de iOS-apps in de top 100 van de App Store Keychain voor het opslaan van authenticatietokens.
Architectonisch is Keychain een versleutelde SQLite-database buiten de sandbox van de app. Elk element (SecItem) wordt versleuteld met een afzonderlijke sleutel, die op zijn beurt wordt beschermd door de hardwaresleutel van Secure Enclave. De systeemdienst Securityd beheert de toegang tot Keychain op basis van app-rechten (entitlements) en de gevraagde beschermingsklasse.
Een belangrijk voordeel van Keychain ten opzichte van andere opslagmethoden: gegevens worden automatisch versleuteld en ontsleuteld door het besturingssysteem. De ontwikkelaar hoeft geen cryptografie handmatig te implementeren — het volstaat om SecItemAdd met de juiste parameters aan te roepen. iOS garandeert dat gegevens uit Keychain niet door andere apps kunnen worden gelezen (bij correcte configuratie van Access Groups).
De architectuur van Keychain omvat meerdere niveaus: fysiek (Secure Enclave), systeem (Security.framework), applicatie (API SecItem*) en logisch (Access Groups, Protection Classes). Inzicht in elk niveau helpt bij het correct ontwerpen van de opslag van geheimen.
De belangrijkste API voor het werken met Keychain zijn de functies van Security framework: SecItemAdd voor toevoegen, SecItemCopyMatching voor lezen, SecItemUpdate voor bijwerken en SecItemDelete voor verwijderen. Elke functie ontvangt een query-woordenboek dat de attributen beschrijft van het gezochte of opgeslagen element.
Belangrijke attributen van query: kSecClass — type element (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount — unieke identificatie binnen de klasse, kSecValueData — opgeslagen gegevens (Data), kSecAttrAccessible — beschermingsklasse. SecItemCopyMatching met de vlag kSecReturnData retourneert de gegevens van het element, met kSecMatchLimit — het aantal resultaten.
Belangrijk: alle functies retourneren een OSStatus. Een geslaagde bewerking retourneert errSecSuccess (0). Fouten: errSecItemNotFound (-25300) — element niet gevonden, errSecDuplicateItem (-25299) — element bestaat al, errSecAuthFailed (-25293) — biometrische authenticatie mislukt. De ontwikkelaar moet elke status correct afhandelen.
Protection Class — het attribuut kSecAttrAccessible, dat bepaalt wanneer gegevens in Keychain beschikbaar zijn voor lezen. iOS ondersteunt zes beschermingsklassen met verschillende niveaus van beschikbaarheid en beveiliging.
De aanbevolen klasse voor de meeste scenario's is kSecAttrAccessibleWhenUnlockedThisDeviceOnly: gegevens zijn alleen beschikbaar bij ontgrendeld apparaat en worden niet gekopieerd naar iCloud Backup. Voor gegevens die beschikbaar moeten zijn na herstart (maar pas na de eerste ontgrendeling), gebruikt u kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Voor kritieke gegevens die bij elke toegang biometrische authenticatie vereisen, combineert u kSecAttrAccessibleWhenUnlockedThisDeviceOnly met een ACL die biometrie vereist.
Klassen zonder het achtervoegsel ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) staan kopiëren naar iCloud Backup toe. Dit is handig voor de gebruiker, maar vermindert de beveiliging — gegevens kunnen worden hersteld uit de back-up. Gebruik voor authenticatietokens altijd ThisDeviceOnly.
Access Control List (ACL) — een mechanisme dat bewerkingen op een Keychain-element beperkt op basis van gebruikersauthenticatie. ACL wordt ingesteld via SecAccessControlCreateWithFlags en doorgegeven in het attribuut kSecAttrAccessControl bij het opslaan van het element.
Ondersteunde vlaggen: kSecAccessControlUserPresence — elke authenticatie (Face ID, Touch ID of toegangscode), kSecAccessControlBiometryCurrentSet — alleen biometrie (huidig geregistreerde vingerafdrukken of gezicht), kSecAccessControlDevicePasscode — alleen toegangscode. ACL wordt toegepast op elke bewerking: lezen, bijwerken en verwijderen van een element vereisen ook authenticatie.
In iOS 15+ is de vlag kSecAccessControlWatch toegevoegd — voor Apple Watch, die authenticatie via het gekoppelde horloge mogelijk maakt. ACL kan worden gecombineerd: bijvoorbeeld kSecAccessControlUserPresence of kSecAccessControlBiometryAny met optionele toegangscode (.or oriëntatie).
iOS Keychain ondersteunt vier hoofdklassen van elementen (kSecClass), elk bestemd voor een eigen gegevenstype. De juiste keuze van de klasse vereenvoudigt de organisatie en het zoeken van elementen.
kSecClassGenericPassword — algemeen wachtwoord: de meest gebruikte klasse. Slaat willekeurige binaire gegevens (Data) op met een unieke sleutel (kSecAttrAccount). Geschikt voor tokens, API-sleutels, PIN-codes. Vereist geen extra entitlements voor gebruik.
kSecClassInternetPassword — internetwachtwoord: slaat gegevens op die zijn gekoppeld aan een netwerkbron. Extra attributen: kSecAttrServer (serverdomein), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. iOS kan dergelijke wachtwoorden automatisch invullen via AutoFill.
kSecClassKey — cryptografische sleutel: voor het opslaan van versleutelingssleutels (AES, RSA, EC). De sleutel wordt opgeslagen als SecKeyRef, niet als Data. kSecClassCertificate — X.509-certificaat voor het opslaan en verifiëren van digitale certificaten. Beide klassen vereisen inzicht in cryptografische bewerkingen en correcte configuratie van attributen.
In de praktijk wordt 95% van het gebruik van Keychain in mobiele apps gedekt door kSecClassGenericPassword voor het opslaan van authenticatietokens en kSecClassKey voor het opslaan van privé versleutelingssleutels. kSecClassCertificate wordt zelden gebruikt — meestal in bedrijfsapps met een eigen PKI.
Laten we praktische voorbeelden bekijken van werken met Keychain in Swift met behulp van Security framework. Elk voorbeeld bevat foutafhandeling en correcte configuratie van Protection Class.
Het basisvoorbeeld slaat een authenticatietoken op in Keychain met bescherming WhenUnlockedThisDeviceOnly. De sleutel (kSecAttrAccount) is de service-identificatie, de gegevens (kSecValueData) zijn het token in Data-formaat.
import Security
enum KeychainError: Error {
case unexpectedStatus(OSStatus)
}
func saveToken(token: String, service: String) throws {
let data = Data(token.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecValueData as String: data,
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
func readToken(service: String) throws -> String {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(
query as CFDictionary, &result
)
guard status == errSecSuccess,
let data = result as? Data else {
throw KeychainError.unexpectedStatus(status)
}
return String(decoding: data, as: UTF8.self)
}
Het voorbeeld demonstreert het gebruik van SecAccessControlCreateWithFlags om de sleutel aan biometrie te koppelen. Elke toegang tot het element vereist Face ID of Touch ID.
import LocalAuthentication
func saveWithBiometry(data: Data, key: String) throws {
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessControl as String: accessControl as Any
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
Het voorbeeld toont de configuratie van Access Group voor gedeelde toegang tot Keychain tussen apps van dezelfde ontwikkelaar. Vereist entitlement keychain-access-groups.
// Capabilities: Keychain Sharing ingeschakeld
// App IDs: group.com.example.shared
func saveSharedToken(token: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "shared_token",
kSecValueData as String: token,
kSecAttrAccessGroup as String:
"group.com.example.shared",
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
}
Correct gebruik van iOS Keychain vereist het naleven van enkele belangrijke regels die typische kwetsbaarheden en gegevensverlies voorkomen.
Gebruik ThisDeviceOnly voor alle authenticatiegeheimen: kSecAttrAccessibleWhenUnlockedThisDeviceOnly garandeert dat tokens niet in iCloud Backup terechtkomen. Als een aanvaller toegang krijgt tot de back-up, zullen Keychain-gegevens met deze vlag niet beschikbaar zijn. Uitzondering zijn gegevens die beschikbaar moeten zijn op alle apparaten van de gebruiker (bijvoorbeeld versleutelingssleutels voor eigen diensten), waarvoor u kSecAttrAccessibleWhenUnlocked met kSecAttrSynchronizable gebruikt.
Bewaar geen ruwe wachtwoorden — bewaar hashes of sessietokens. Apple Security Guide (2025) raadt aan om nooit het wachtwoord van de gebruiker in Keychain in platte tekst op te slaan. Bewaar in plaats daarvan de refresh token die van de server is ontvangen na succesvolle authenticatie via OAuth 2.0. Het wachtwoord wordt alleen gebruikt om de token te verkrijgen en wordt onmiddellijk uit het geheugen verwijderd.
Verwerk Keychain-fouten correct: elke bewerking met Keychain retourneert een OSStatus die moet worden gecontroleerd. Speciale aandacht — errSecItemNotFound (token verlopen of verwijderd) en errSecAuthFailed (biometrie mislukt). In het eerste geval moet de app om nieuwe authenticatie vragen, in het tweede — de gebruiker een alternatieve methode tonen (toegangscode). Nooit de status errSecItemNotFound negeren — dit leidt tot een crash van de app bij het lezen van nil.
Test Keychain op een echt apparaat: de simulator heeft geen Secure Enclave en ondersteunt geen biometrische ACL's. Controleer altijd de scenario's: eerste start, herstel vanuit back-up, wijzigen van apparaatwachtwoord, verwijderen en opnieuw installeren van de app. Op een echt apparaat blijft Keychain behouden na het verwijderen van de app, maar alleen als de vlag kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly niet is gebruikt — deze vlag wordt gewist bij het verwijderen van de toegangscode.
Minimaliseer het aantal bewerkingen met Keychain: elke lees- of schrijfbewerking is een aanroep van de systeemdienst Securityd, die de thread kan blokkeren. Cache gelezen tokens in het geheugen voor de duur van de sessie en benader Keychain opnieuw alleen bij herstart van de app of authenticatiefout (401 van de server). iOS blokkeert Keychain automatisch bij vergrendeling van het apparaat, plan daarom lezen via LAContext met een verzoek om biometrie.
Veelgestelde vragen
Ja, gebruik de beschermingsklasse kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly of kSecAttrAccessibleAfterFirstUnlock. De gegevens zijn beschikbaar na de eerste ontgrendeling van het apparaat na herstart. Voor automatische toegang bij het starten van de app (zonder te wachten op ontgrendeling) gebruikt u kSecAttrAccessibleAlways, maar dit vermindert de beveiliging.
Roep SecItemDelete aan met een query die kSecClass bevat voor elk gegevenstype. Voor het volledig wissen van alle elementen van de app: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Herhaal voor kSecClassKey, kSecClassCertificate en kSecClassInternetPassword.
kSecAttrAccessible bepaalt wanneer gegevens beschikbaar zijn (bij ontgrendeling, na eerste ontgrendeling, enz.). kSecAttrAccessControl bepaalt wie toegang kan krijgen (biometrie, toegangscode, elke authenticatie). Ze worden gecombineerd: eerst Protection Class, dan ACL. Bijvoorbeeld, gegevens zijn alleen beschikbaar bij ontgrendeling EN alleen na Face ID.
Oorzaken: het element is nooit opgeslagen, het element is gewist bij het verwijderen van de toegangscode (als kSecAttrAccessibleWhenPasscodeSet is gebruikt), de app is opnieuw geïnstalleerd (Keychain blijft behouden, maar wordt niet hersteld uit back-up op een nieuw apparaat), de Access Group of identificatie van het ontwikkelingsteam is gewijzigd. Controleer kSecAttrService en kSecAttrAccount.
Gebruik LAContext uit LocalAuthentication: roep context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) aan. Als het true retourneert — ondersteunt het apparaat Touch ID of Face ID. Gebruik voor Keychain ACL de vlag biometryCurrentSet (alleen huidige biometrische gegevens) of biometryAny (eerder geregistreerde gegevens).
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook