Keychain in iOS: What It Is, Architecture, and Working with Secrets

Author: IT Sectr Published: 2026-04-04 Reading time: 9 min

Keychain is a secure storage in iOS designed for safely storing passwords, cryptographic keys, certificates, and confidential notes. According to Apple Security Documentation (2025), Keychain uses hardware encryption through Secure Enclave on all devices with A7 chip and newer. Understanding the architecture of iOS Keychain is essential for every developer to properly store application tokens and secrets.

Key Takeaways

  • iOS Keychain is an encrypted SQLite database for storing secrets with hardware protection through Secure Enclave.
  • Protection Class determines when data is available: when the device is unlocked, after first unlock, or always.
  • Access Control List (ACL) is a mechanism for restricting access to Keychain items, including biometric authentication.
  • SecItemAdd and SecItemCopyMatching are the main Security framework APIs for writing and reading items.
  • kSecAttrSynchronizable is the flag that enables Keychain synchronization via iCloud for access across all user devices.

What Is Keychain in iOS?

iOS Keychain is a secure mechanism for storing confidential data, built into Apple's operating system. Unlike UserDefaults or regular files, Keychain encrypts all items at the hardware level and provides fine-grained access control based on security policies.

Keychain was introduced in iOS 2.0 and has undergone significant changes since then: iOS 7 added hardware key support through Secure Enclave, iOS 9 introduced Keychain sharing between apps via Access Groups, iOS 13 added biometric binding support through LAContext. According to Apple WWDC Session (2024), over 90% of iOS apps in the top 100 of the App Store use Keychain for storing authentication tokens.

Architecturally, Keychain is an encrypted SQLite database located outside the application sandbox. Each item (SecItem) is encrypted with a separate key, which in turn is protected by the Secure Enclave hardware key. The system service Securityd manages access to Keychain based on application entitlements and the requested protection class.

An important advantage of Keychain over other storage methods: data is automatically encrypted and decrypted by the OS. The developer does not need to implement cryptography manually - just call SecItemAdd with the correct parameters. iOS guarantees that data from Keychain cannot be read by other applications (when Access Groups are properly configured).

Keychain Architecture

The Keychain architecture includes several levels: physical (Secure Enclave), system (Security.framework), application (SecItem* API), and logical (Access Groups, Protection Classes). Understanding each level helps properly design secret storage.

SecItemAdd and SecItemCopyMatching

The main API for working with Keychain is the Security framework functions: SecItemAdd for adding, SecItemCopyMatching for reading, SecItemUpdate for updating, and SecItemDelete for deleting. Each function takes a query dictionary that describes the attributes of the item to find or store.

Key query attributes: kSecClass - item type (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount - unique identifier within the class, kSecValueData - stored data (Data), kSecAttrAccessible - protection class. SecItemCopyMatching with the kSecReturnData flag returns item data, with kSecMatchLimit - the number of results.

Important: all functions return an OSStatus. A successful operation returns errSecSuccess (0). Errors: errSecItemNotFound (-25300) - item not found, errSecDuplicateItem (-25299) - item already exists, errSecAuthFailed (-25293) - biometric authentication failed. The developer must handle each status correctly.

Protection Classes

Protection Class is the kSecAttrAccessible attribute that determines when data in Keychain is available for reading. iOS supports six protection classes with different levels of availability and security.

The recommended class for most scenarios is kSecAttrAccessibleWhenUnlockedThisDeviceOnly: data is only available when the device is unlocked and is not copied to iCloud Backup. For data that should be available after reboot (but only after first unlock), use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. For critical data requiring biometric authentication on each access, combine kSecAttrAccessibleWhenUnlockedThisDeviceOnly with an ACL that requires biometry.

Classes without the ThisDeviceOnly suffix (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) allow copying to iCloud Backup. This is convenient for the user but reduces security - data can be restored from backup. For authentication tokens, always use ThisDeviceOnly.

Access Control Lists (ACL)

Access Control List (ACL) is a mechanism that restricts operations on a Keychain item based on user authentication. ACL is set via SecAccessControlCreateWithFlags and passed to the kSecAttrAccessControl attribute when saving an item.

Supported flags: kSecAccessControlUserPresence - any authentication (Face ID, Touch ID, or passcode), kSecAccessControlBiometryCurrentSet - biometry only (currently registered fingerprints or face), kSecAccessControlDevicePasscode - passcode only. ACL applies to every operation: reading, updating, and deleting an item also require authentication.

On iOS 15+, the flag kSecAccessControlWatch appeared - for Apple Watch, which allows authentication through a paired watch. ACL can be combined: for example, kSecAccessControlUserPresence or kSecAccessControlBiometryAny with an optional passcode (.or orientation).

Data Types in Keychain

iOS Keychain supports four main item classes (kSecClass), each designed for its own data type. Choosing the right class simplifies organization and item search.

kSecClassGenericPassword - generic password: the most commonly used class. Stores arbitrary binary data (Data) with a unique key (kSecAttrAccount). Suitable for tokens, API keys, PIN codes. Does not require additional entitlements to use.

kSecClassInternetPassword - internet password: stores data associated with a network resource. Additional attributes: kSecAttrServer (server domain), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. iOS can automatically fill such passwords through AutoFill.

kSecClassKey - cryptographic key: for storing encryption keys (AES, RSA, EC). The key is stored as SecKeyRef, not as Data. kSecClassCertificate - X.509 certificate for storing and verifying digital certificates. Both classes require understanding of cryptographic operations and proper attribute configuration.

In practice, 95% of Keychain usage in mobile apps is covered by kSecClassGenericPassword for storing authentication tokens and kSecClassKey for storing private encryption keys. kSecClassCertificate is rarely used - typically in enterprise apps with their own PKI.

Code Examples: Working with Keychain in Swift

Let's look at practical examples of working with Keychain in Swift using the Security framework. Each example includes error handling and proper Protection Class configuration.

Saving and Reading a Token

A basic example saves an authentication token in Keychain with WhenUnlockedThisDeviceOnly protection. The key (kSecAttrAccount) is the service identifier, the data (kSecValueData) is the token in Data format.

swift
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)
}

Saving with Biometric Binding

The example demonstrates using SecAccessControlCreateWithFlags to bind a key to biometry. Each access to the item will require Face ID or Touch ID.

swift
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)
    }
}

Keychain Sharing Between Apps

The example shows configuring an Access Group for shared Keychain access between apps from the same developer. Requires the keychain-access-groups entitlement.

swift
// Capabilities: Keychain Sharing Enabled
// 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)
}

Best Practices for Working with Keychain

Proper use of iOS Keychain requires following several key rules that prevent common vulnerabilities and data loss.

Use ThisDeviceOnly for all authentication secrets: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ensures tokens do not end up in iCloud Backup. If an attacker gains access to the backup, Keychain data with this flag will not be available. The exception is data that should be available on all user devices (e.g., encryption keys for proprietary services), for which use kSecAttrAccessibleWhenUnlocked with kSecAttrSynchronizable.

Do not store raw passwords - store hashes or session tokens. Apple Security Guide (2025) recommends never saving the user password in Keychain in plain text. Instead, save the refresh token received from the server after successful authentication via OAuth 2.0. The password is only used to obtain the token and is immediately removed from memory.

Handle Keychain errors correctly: each Keychain operation returns an OSStatus that must be checked. Pay special attention to errSecItemNotFound (token expired or deleted) and errSecAuthFailed (biometry failed). In the first case, the app should request new authentication; in the second, show the user an alternative method (passcode). Never ignore the errSecItemNotFound status - it will cause a crash when trying to read nil.

Test Keychain on a real device: the simulator does not have a Secure Enclave and does not support biometric ACLs. Always check scenarios: first launch, restore from backup, device password change, app deletion and reinstallation. On a real device, Keychain persists when the app is deleted, but only if the kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly flag was not used, which is cleared when the passcode is removed.

Minimize the number of Keychain operations: each read or write operation is a call to the system service Securityd, which can block the thread. Cache read tokens in memory for the session duration and access Keychain again only on app restart or authentication error (401 from the server). iOS automatically locks Keychain when the device is locked, so plan reading through LAContext with a biometric request.

Frequently Asked Questions

Can I save data to Keychain and read it after a reboot?

Yes, use the protection class kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly or kSecAttrAccessibleAfterFirstUnlock. Data will be available after the first device unlock following a reboot. For automatic access at app launch (without waiting for unlock), use kSecAttrAccessibleAlways, but this reduces security.

How do I clear Keychain on user logout?

Call SecItemDelete with a query containing kSecClass for each data type. For complete cleanup of all app items, execute: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Repeat for kSecClassKey, kSecClassCertificate, and kSecClassInternetPassword.

What is the difference between kSecAttrAccessible and kSecAttrAccessControl?

kSecAttrAccessible determines when data is available (on unlock, after first unlock, etc.). kSecAttrAccessControl determines who can access it (biometry, passcode, any authentication). They combine: first Protection Class, then ACL. For example, data is only available on unlock AND only after Face ID.

Why does SecItemCopyMatching return errSecItemNotFound?

Reasons: the item was never saved, the item was deleted when the passcode was removed (if kSecAttrAccessibleWhenPasscodeSet was used), the app was reinstalled (Keychain persists but is not restored from backup on a new device), the Access Group or developer team identifier changed. Check kSecAttrService and kSecAttrAccount.

How do I check if the device supports biometry in Keychain?

Use LAContext from LocalAuthentication: call context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil). If it returns true - the device supports Touch ID or Face ID. For Keychain ACL, use the biometryCurrentSet flag (only current biometric data) or biometryAny (any previously registered).

Summary

  • iOS Keychain is a hardware-protected storage for secrets with encryption through Secure Enclave and access control via ACL.
  • Security framework provides SecItemAdd, SecItemCopyMatching, SecItemUpdate, and SecItemDelete functions for working with items.
  • Protection Class (kSecAttrAccessible) is chosen based on the scenario: WhenUnlockedThisDeviceOnly is the standard for tokens.
  • ACL with biometry (kSecAttrAccessControl) adds a Face ID or Touch ID requirement for each read operation.
  • kSecClassGenericPassword covers 95% of cases - storing tokens, API keys, PIN codes, and notes.
  • ThisDeviceOnly prevents copying secrets to iCloud Backup - mandatory for authentication tokens.
  • Proper OSStatus handling and testing on a real device are essential practices for reliable Keychain usage.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also