Keychain în iOS: ce este, arhitectură și lucrul cu secrete

Autor: IT Sectr Publicat: 2026-04-04 Timp de citire: 9 min

Keychain (Înel de chei) — un depozit protejat în iOS, destinat stocării sigure a parolelor, cheilor criptografice, certificatelor și notițelor confidențiale. Conform Apple Security Documentation (2025), Keychain utilizează criptarea hardware prin Secure Enclave pe toate dispozitivele cu cip A7 și mai noi. Înțelegerea arhitecturii iOS Keychain este necesară fiecărui dezvoltator pentru stocarea corectă a tokenurilor și secretelor aplicației.

Principalele

  • iOS Keychain — bază de date SQLite criptată pentru stocarea secretelor cu protecție hardware prin Secure Enclave.
  • Protection Class determină când datele sunt disponibile: la dispozitiv deblocat, după prima deblocare sau întotdeauna.
  • Access Control List (ACL) — mecanism de restricționare a accesului la elementele Keychain, inclusiv autentificare biometrică.
  • SecItemAdd și SecItemCopyMatching — API-urile principale Security framework pentru scrierea și citirea elementelor.
  • kSecAttrSynchronizable — flag care permite sincronizarea Keychain prin iCloud pentru acces pe toate dispozitivele utilizatorului.

Ce este Keychain în iOS?

iOS Keychain — un mecanism protejat de stocare a datelor confidențiale, integrat în sistemul de operare Apple. Spre deosebire de UserDefaults sau fișierele obișnuite, Keychain criptează toate elementele la nivel hardware și oferă un control fin al accesului bazat pe politici de securitate.

Keychain a fost introdus în iOS 2.0 și de atunci a suferit modificări semnificative: în iOS 7 s-a adăugat suportul pentru chei hardware prin Secure Enclave, în iOS 9 — separarea Keychain între aplicații prin Access Groups, în iOS 13 — suportul pentru legătura biometrică prin LAContext. Conform Apple WWDC Session (2024), peste 90% din aplicațiile iOS din top 100 App Store folosesc Keychain pentru stocarea tokenurilor de autentificare.

Arhitectural, Keychain este o bază de date SQLite criptată, situată în afara sandbox-ului aplicației. Fiecare element (SecItem) este criptat cu o cheie separată, care la rândul său este protejată de cheia hardware Secure Enclave. Serviciul de sistem Securityd gestionează accesul la Keychain pe baza drepturilor aplicației (entitlements) și a clasei de protecție solicitate.

Un avantaj important al Keychain față de alte metode de stocare: datele sunt automat criptate și decriptate de sistemul de operare. Dezvoltatorul nu trebuie să implementeze criptografia manual — este suficient să apeleze SecItemAdd cu parametrii corecți. iOS garantează că datele din Keychain nu pot fi citite de alte aplicații (la configurarea corectă a Access Groups).

Arhitectura Keychain

Arhitectura Keychain include mai multe niveluri: fizic (Secure Enclave), de sistem (Security.framework), aplicativ (API SecItem*) și logic (Access Groups, Protection Classes). Înțelegerea fiecărui nivel ajută la proiectarea corectă a stocării secretelor.

SecItemAdd și SecItemCopyMatching

API-ul principal pentru lucrul cu Keychain — funcțiile Security framework: SecItemAdd pentru adăugare, SecItemCopyMatching pentru citire, SecItemUpdate pentru actualizare și SecItemDelete pentru ștergere. Fiecare funcție primește un dicționar query care descrie atributele elementului căutat sau salvat.

Atributele cheie ale query: kSecClass — tipul elementului (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount — identificator unic în cadrul clasei, kSecValueData — datele stocate (Data), kSecAttrAccessible — clasa de protecție. SecItemCopyMatching cu flagul kSecReturnData returnează datele elementului, cu kSecMatchLimit — numărul de rezultate.

Important: toate funcțiile returnează statusul OSStatus. O operațiune reușită returnează errSecSuccess (0). Erori: errSecItemNotFound (-25300) — elementul nu a fost găsit, errSecDuplicateItem (-25299) — elementul există deja, errSecAuthFailed (-25293) — autentificarea biometrică a eșuat. Dezvoltatorul trebuie să gestioneze corect fiecare status.

Clase de protecție (Protection Class)

Protection Class — atributul kSecAttrAccessible, care determină când datele din Keychain sunt disponibile pentru citire. iOS suportă șase clase de protecție cu diferite niveluri de disponibilitate și securitate.

Clasa recomandată pentru majoritatea scenariilor este kSecAttrAccessibleWhenUnlockedThisDeviceOnly: datele sunt disponibile doar la dispozitiv deblocat și nu sunt copiate în iCloud Backup. Pentru datele care trebuie să fie disponibile după repornire (dar numai după prima deblocare), utilizați kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Pentru datele critice care necesită autentificare biometrică la fiecare acces, combinați kSecAttrAccessibleWhenUnlockedThisDeviceOnly cu ACL care necesită biometrie.

Clasele fără sufixul ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) permit copierea în iCloud Backup. Acest lucru este convenabil pentru utilizator, dar reduce securitatea — datele pot fi recuperate din backup. Pentru tokenurile de autentificare, utilizați întotdeauna ThisDeviceOnly.

Access Control Lists (ACL)

Access Control List (ACL) — mecanism care restricționează operațiile cu un element Keychain pe baza autentificării utilizatorului. ACL se setează prin SecAccessControlCreateWithFlags și se transmite în atributul kSecAttrAccessControl la salvarea elementului.

Flaguri suportate: kSecAccessControlUserPresence — orice autentificare (Face ID, Touch ID sau cod de acces), kSecAccessControlBiometryCurrentSet — doar biometrie (amprentele sau fața înregistrate curent), kSecAccessControlDevicePasscode — doar codul de acces. ACL se aplică fiecărei operații: citirea, actualizarea și ștergerea elementului necesită de asemenea autentificare.

În iOS 15+ a apărut flagul kSecAccessControlWatch — pentru Apple Watch, care permite autentificarea prin ceasul asociat. ACL poate fi combinat: de exemplu, kSecAccessControlUserPresence sau kSecAccessControlBiometryAny cu cod de acces opțional (orientarea .or).

Tipuri de date în Keychain

iOS Keychain suportĄ patru clase principale de elemente (kSecClass), fiecare destinată propriului tip de date. Alegerea corectă a clasei simplifică organizarea și căutarea elementelor.

kSecClassGenericPassword — parola generală: cea mai frecvent utilizată clasă. Stochează date binare arbitrare (Data) cu o cheie unică (kSecAttrAccount). Potrivită pentru tokenuri, chei API, coduri PIN. Nu necesită entitlements suplimentare pentru utilizare.

kSecClassInternetPassword — parola de internet: stochează date asociate cu o resursă de rețea. Atribute suplimentare: kSecAttrServer (domeniul serverului), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. iOS poate completa automat astfel de parole prin AutoFill.

kSecClassKey — cheie criptografică: pentru stocarea cheilor de criptare (AES, RSA, EC). Cheia este stocată ca SecKeyRef, nu ca Data. kSecClassCertificate — certificat X.509 pentru stocarea și verificarea certificatelor digitale. Ambele clase necesită înțelegerea operațiunilor criptografice și configurarea corectă a atributelor.

În practică, 95% din cazurile de utilizare a Keychain în aplicații mobile sunt acoperite de kSecClassGenericPassword pentru stocarea tokenurilor de autentificare și kSecClassKey pentru stocarea cheilor private de criptare. kSecClassCertificate este utilizat rar — de obicei în aplicații corporative cu propriul PKI.

Exemple de cod: lucrul cu Keychain în Swift

Să examinăm exemple practice de lucru cu Keychain în Swift utilizând Security framework. Fiecare exemplu include gestionarea erorilor și configurarea corectă a Protection Class.

Salvarea și citirea tokenului

Exemplul de bază salvează un token de autentificare în Keychain cu protecție WhenUnlockedThisDeviceOnly. Cheia (kSecAttrAccount) — identificatorul serviciului, datele (kSecValueData) — tokenul în format Data.

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

Salvarea cu legătură biometrică

Exemplul demonstrează utilizarea SecAccessControlCreateWithFlags pentru legarea cheii de biometrie. Fiecare acces la element va necesita Face ID sau 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 între aplicații

Exemplul arată configurarea Access Group pentru accesul partajat la Keychain între aplicațiile aceluiași dezvoltator. Necesită entitlement keychain-access-groups.

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

Cele mai bune practici de lucru cu Keychain

Utilizarea corectă a iOS Keychain necesită respectarea câtorva reguli cheie care previn vulnerabilitățile tipice și pierderea datelor.

Utilizați ThisDeviceOnly pentru toate secretele de autentificare: kSecAttrAccessibleWhenUnlockedThisDeviceOnly garantează că tokenurile nu vor ajunge în iCloud Backup. Dacă un atacator obține acces la backup, datele Keychain cu acest flag nu vor fi disponibile. Excepție o fac datele care trebuie să fie disponibile pe toate dispozitivele utilizatorului (de exemplu, chei de criptare pentru servicii proprii), pentru care utilizați kSecAttrAccessibleWhenUnlocked cu kSecAttrSynchronizable.

Nu stocați parole în formă brută — stocați hashuri sau tokenuri de sesiune. Apple Security Guide (2025) recomandă să nu salvați niciodată parola utilizatorului în Keychain în text clar. În schimb, salvați refresh token-ul obținut de la server după autentificarea reușită prin OAuth 2.0. Parola este utilizată doar pentru obținerea tokenului și este imediat ștearsă din memorie.

Gestionați corect erorile Keychain: fiecare operație cu Keychain returnează OSStatus care trebuie verificat. O atenție deosebită — errSecItemNotFound (tokenul a expirat sau a fost șters) și errSecAuthFailed (biometria a eșuat). În primul caz, aplicația trebuie să solicite o nouă autentificare, în al doilea — să arate utilizatorului o metodă alternativă (cod de acces). Niciodată nu ignorați statusul errSecItemNotFound — aceasta va duce la prăbușirea aplicației la încercarea de a citi nil.

Testați Keychain pe un dispozitiv real: simulatorul nu are Secure Enclave și nu suportă ACL-uri biometrice. Verificați întotdeauna scenariile: prima lansare, restaurarea din backup, schimbarea parolei dispozitivului, ștergerea și reinstalarea aplicației. Pe un dispozitiv real, Keychain se păstrează după ștergerea aplicației, dar numai dacă nu s-a utilizat flagul kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, care se curăță la eliminarea codului de acces.

Minimizați numărul de operații cu Keychain: fiecare operație de citire sau scriere este o apelare a serviciului de sistem Securityd, care poate bloca firul de execuție. Cache-uiți tokenurile citite în memorie pe durata sesiunii și accesați Keychain din nou doar la repornirea aplicației sau la eroare de autentificare (401 de la server). iOS blochează automat Keychain la blocarea dispozitivului, de aceea planificați citirea prin LAContext cu solicitare de biometrie.

Întrebări frecvente

Pot salva date în Keychain și să le citesc după repornire?

Da, utilizați clasa de protecție kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly sau kSecAttrAccessibleAfterFirstUnlock. Datele vor fi disponibile după prima deblocare a dispozitivului după repornire. Pentru accesul automat la pornirea aplicației (fără a aștepta deblocarea), utilizați kSecAttrAccessibleAlways, dar aceasta reduce securitatea.

Cum curăț Keychain la deconectarea utilizatorului?

Apelați SecItemDelete cu query care conține kSecClass pentru fiecare tip de date. Pentru curățarea completă a tuturor elementelor aplicației, executați: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Repetați pentru kSecClassKey, kSecClassCertificate și kSecClassInternetPassword.

Care este diferența dintre kSecAttrAccessible și kSecAttrAccessControl?

kSecAttrAccessible determină când datele sunt disponibile (la deblocare, după prima deblocare etc.). kSecAttrAccessControl determină cine poate obține acces (biometrie, cod de acces, orice autentificare). Ele se combină: mai întâi Protection Class, apoi ACL. De exemplu, datele sunt disponibile doar la deblocare ȘI numai după Face ID.

De ce SecItemCopyMatching returnează errSecItemNotFound?

Cauze: elementul nu a fost niciodată salvat, elementul a fost șters la eliminarea codului de acces (dacă s-a utilizat kSecAttrAccessibleWhenPasscodeSet), aplicația a fost reinstalată (Keychain se păstrează, dar nu se restaurează din backup pe un dispozitiv nou), s-a schimbat Access Group sau identificatorul echipei de dezvoltare. Verificați kSecAttrService și kSecAttrAccount.

Cum verific dacă dispozitivul suportă biometria în Keychain?

Utilizați LAContext din LocalAuthentication: apelați context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil). Dacă returnează true — dispozitivul suportă Touch ID sau Face ID. Pentru ACL Keychain, utilizați flagul biometryCurrentSet (doar datele biometrice curente) sau biometryAny (orice date înregistrate anterior).

Concluzii

  • iOS Keychain — depozit protejat hardware pentru secrete cu criptare prin Secure Enclave și control al accesului prin ACL.
  • Security framework oferă funcțiile SecItemAdd, SecItemCopyMatching, SecItemUpdate și SecItemDelete pentru lucrul cu elementele.
  • Protection Class (kSecAttrAccessible) se alege pe baza scenariului: WhenUnlockedThisDeviceOnly — standard pentru tokenuri.
  • ACL cu biometrie (kSecAttrAccessControl) adaugă cerința Face ID sau Touch ID pentru fiecare operație de citire.
  • kSecClassGenericPassword acoperă 95% din cazuri — stocarea tokenurilor, cheilor API, codurilor PIN și notițelor.
  • ThisDeviceOnly previne copierea secretelor în iCloud Backup — obligatoriu pentru tokenurile de autentificare.
  • Gestionarea corectă a OSStatus și testarea pe dispozitiv real — practici obligatorii pentru o funcționare fiabilă cu Keychain.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și