Keychain en iOS: qué es, arquitectura y trabajo con secretos

Autor: IT Sectr Publicado: 2026-04-04 Tiempo de lectura: 9 min

Keychain es un almacenamiento seguro en iOS diseñado para guardar contraseñas, claves criptográficas, certificados y notas confidenciales de forma segura. Según Apple Security Documentation (2025), Keychain utiliza cifrado por hardware a través de Secure Enclave en todos los dispositivos con chip A7 y posteriores. Comprender la arquitectura de iOS Keychain es esencial para que cada desarrollador almacene correctamente los tokens y secretos de la aplicación.

Puntos clave

  • iOS Keychain es una base de datos SQLite cifrada para almacenar secretos con protección por hardware a través de Secure Enclave.
  • Protection Class determina cuándo los datos están disponibles: cuando el dispositivo está desbloqueado, después del primer desbloqueo o siempre.
  • Access Control List (ACL) es un mecanismo para restringir el acceso a los elementos de Keychain, incluida la autenticación biométrica.
  • SecItemAdd y SecItemCopyMatching son las principales API de Security framework para escribir y leer elementos.
  • kSecAttrSynchronizable es el indicador que permite la sincronización de Keychain a través de iCloud para el acceso en todos los dispositivos del usuario.

¿Qué es Keychain en iOS?

iOS Keychain es un mecanismo seguro para almacenar datos confidenciales, integrado en el sistema operativo de Apple. A diferencia de UserDefaults o los archivos normales, Keychain cifra todos los elementos a nivel de hardware y proporciona un control de acceso detallado basado en políticas de seguridad.

Keychain se introdujo en iOS 2.0 y desde entonces ha sufrido cambios significativos: en iOS 7 se agregó soporte para claves de hardware a través de Secure Enclave, en iOS 9 se introdujo el uso compartido de Keychain entre aplicaciones mediante Access Groups, en iOS 13 se agregó soporte para vinculación biométrica a través de LAContext. Según Apple WWDC Session (2024), más del 90% de las aplicaciones iOS en el top 100 de App Store usan Keychain para almacenar tokens de autenticación.

Arquitectónicamente, Keychain es una base de datos SQLite cifrada ubicada fuera del sandbox de la aplicación. Cada elemento (SecItem) se cifra con una clave separada que, a su vez, está protegida por la clave de hardware Secure Enclave. El servicio del sistema Securityd gestiona el acceso a Keychain según los entitlements de la aplicación y la clase de protección solicitada.

Una ventaja importante de Keychain sobre otros métodos de almacenamiento: los datos se cifran y descifran automáticamente por el sistema operativo. El desarrollador no necesita implementar criptografía manualmente, solo llamar a SecItemAdd con los parámetros correctos. iOS garantiza que otros programas no puedan leer los datos de Keychain (cuando Access Groups están configurados correctamente).

Arquitectura de Keychain

La arquitectura de Keychain incluye varios niveles: físico (Secure Enclave), sistema (Security.framework), aplicación (API SecItem*) y lógico (Access Groups, Protection Classes). Comprender cada nivel ayuda a diseñar correctamente el almacenamiento de secretos.

SecItemAdd y SecItemCopyMatching

La API principal para trabajar con Keychain son las funciones de Security framework: SecItemAdd para agregar, SecItemCopyMatching para leer, SecItemUpdate para actualizar y SecItemDelete para eliminar. Cada función acepta un diccionario query que describe los atributos del elemento a buscar o guardar.

Atributos clave de query: kSecClass - tipo de elemento (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount - identificador único dentro de la clase, kSecValueData - datos guardados (Data), kSecAttrAccessible - clase de protección. SecItemCopyMatching con el indicador kSecReturnData devuelve los datos del elemento, con kSecMatchLimit - la cantidad de resultados.

Importante: todas las funciones devuelven un OSStatus. Una operación exitosa devuelve errSecSuccess (0). Errores: errSecItemNotFound (-25300) - elemento no encontrado, errSecDuplicateItem (-25299) - el elemento ya existe, errSecAuthFailed (-25293) - autenticación biométrica fallida. El desarrollador debe manejar cada estado correctamente.

Clases de protección (Protection Class)

Protection Class es el atributo kSecAttrAccessible que determina cuándo los datos en Keychain están disponibles para lectura. iOS admite seis clases de protección con diferentes niveles de disponibilidad y seguridad.

La clase recomendada para la mayoría de escenarios es kSecAttrAccessibleWhenUnlockedThisDeviceOnly: los datos solo están disponibles cuando el dispositivo está desbloqueado y no se copian a iCloud Backup. Para datos que deben estar disponibles después de reiniciar (pero solo después del primer desbloqueo), use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Para datos críticos que requieren autenticación biométrica en cada acceso, combine kSecAttrAccessibleWhenUnlockedThisDeviceOnly con una ACL que requiera biometría.

Las clases sin el sufijo ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) permiten la copia a iCloud Backup. Esto es conveniente para el usuario pero reduce la seguridad: los datos pueden restaurarse desde la copia de seguridad. Para tokens de autenticación, use siempre ThisDeviceOnly.

Listas de control de acceso (ACL)

Access Control List (ACL) es un mecanismo que restringe las operaciones en un elemento de Keychain según la autenticación del usuario. La ACL se configura mediante SecAccessControlCreateWithFlags y se pasa al atributo kSecAttrAccessControl al guardar un elemento.

Indicadores compatibles: kSecAccessControlUserPresence - cualquier autenticación (Face ID, Touch ID o código de acceso), kSecAccessControlBiometryCurrentSet - solo biometría (huellas digitales o rostro actualmente registrados), kSecAccessControlDevicePasscode - solo código de acceso. La ACL se aplica a cada operación: leer, actualizar y eliminar un elemento también requieren autenticación.

En iOS 15+ apareció el indicador kSecAccessControlWatch - para Apple Watch, que permite la autenticación a través de un reloj emparejado. La ACL se puede combinar: por ejemplo, kSecAccessControlUserPresence o kSecAccessControlBiometryAny con un código de acceso opcional (orientación .or).

Tipos de datos en Keychain

iOS Keychain admite cuatro clases principales de elementos (kSecClass), cada una diseñada para su propio tipo de datos. Elegir la clase correcta simplifica la organización y la búsqueda de elementos.

kSecClassGenericPassword - contraseña genérica: la clase más utilizada. Almacena datos binarios arbitrarios (Data) con una clave única (kSecAttrAccount). Adecuado para tokens, claves de API, códigos PIN. No requiere entitlements adicionales para su uso.

kSecClassInternetPassword - contraseña de internet: almacena datos asociados con un recurso de red. Atributos adicionales: kSecAttrServer (dominio del servidor), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. iOS puede completar automáticamente estas contraseñas mediante AutoFill.

kSecClassKey - clave criptográfica: para almacenar claves de cifrado (AES, RSA, EC). La clave se guarda como SecKeyRef, no como Data. kSecClassCertificate - certificado X.509 para almacenar y verificar certificados digitales. Ambas clases requieren comprensión de operaciones criptográficas y configuración correcta de atributos.

En la práctica, el 95% de los casos de uso de Keychain en aplicaciones móviles se cubren con kSecClassGenericPassword para almacenar tokens de autenticación y kSecClassKey para almacenar claves de cifrado privadas. kSecClassCertificate se usa raramente, generalmente en aplicaciones empresariales con PKI propio.

Ejemplos de código: trabajar con Keychain en Swift

Veamos ejemplos prácticos de trabajo con Keychain en Swift usando Security framework. Cada ejemplo incluye manejo de errores y configuración correcta de Protection Class.

Guardar y leer un token

Un ejemplo básico guarda un token de autenticación en Keychain con protección WhenUnlockedThisDeviceOnly. La clave (kSecAttrAccount) es el identificador del servicio, los datos (kSecValueData) son el token en formato 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)
}

Guardar con vinculación biométrica

El ejemplo demuestra el uso de SecAccessControlCreateWithFlags para vincular una clave a la biometría. Cada acceso al elemento requerirá Face ID o 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 entre aplicaciones

El ejemplo muestra la configuración de un Access Group para el acceso compartido a Keychain entre aplicaciones del mismo desarrollador. Requiere el entitlement keychain-access-groups.

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

Mejores prácticas para trabajar con Keychain

El uso correcto de iOS Keychain requiere seguir varias reglas clave que previenen vulnerabilidades comunes y pérdida de datos.

Use ThisDeviceOnly para todos los secretos de autenticación: kSecAttrAccessibleWhenUnlockedThisDeviceOnly garantiza que los tokens no terminen en iCloud Backup. Si un atacante obtiene acceso a la copia de seguridad, los datos de Keychain con este indicador no estarán disponibles. La excepción son los datos que deben estar disponibles en todos los dispositivos del usuario (por ejemplo, claves de cifrado para servicios propietarios), para los cuales use kSecAttrAccessibleWhenUnlocked con kSecAttrSynchronizable.

No almacene contraseñas en texto plano, guarde hashes o tokens de sesión. La Apple Security Guide (2025) recomienda nunca guardar la contraseña del usuario en Keychain en texto claro. En su lugar, guarde el refresh token recibido del servidor después de una autenticación exitosa mediante OAuth 2.0. La contraseña solo se usa para obtener el token y se elimina inmediatamente de la memoria.

Maneje los errores de Keychain correctamente: cada operación de Keychain devuelve un OSStatus que debe verificarse. Preste especial atención a errSecItemNotFound (token caducado o eliminado) y errSecAuthFailed (biometría fallida). En el primer caso, la aplicación debe solicitar una nueva autenticación; en el segundo, mostrar al usuario un método alternativo (código de acceso). Nunca ignore el estado errSecItemNotFound, provocará un fallo al intentar leer nil.

Pruebe Keychain en un dispositivo real: el simulador no tiene Secure Enclave ni admite ACL biométricos. Verifique siempre los escenarios: primer inicio, restauración desde copia de seguridad, cambio de contraseña del dispositivo, eliminación y reinstalación de la aplicación. En un dispositivo real, Keychain persiste cuando se elimina la aplicación, pero solo si no se usó el indicador kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, que se borra al eliminar el código de acceso.

Minimice la cantidad de operaciones con Keychain: cada operación de lectura o escritura es una llamada al servicio del sistema Securityd, que puede bloquear el hilo. Almacene en caché los tokens leídos en memoria durante la sesión y acceda a Keychain nuevamente solo al reiniciar la aplicación o en caso de error de autenticación (401 del servidor). iOS bloquea automáticamente Keychain cuando el dispositivo se bloquea, por lo tanto, planifique la lectura a través de LAContext con una solicitud biométrica.

Preguntas frecuentes

¿Puedo guardar datos en Keychain y leerlos después de reiniciar?

Sí, use la clase de protección kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly o kSecAttrAccessibleAfterFirstUnlock. Los datos estarán disponibles después del primer desbloqueo del dispositivo tras un reinicio. Para acceso automático al iniciar la aplicación (sin esperar el desbloqueo), use kSecAttrAccessibleAlways, pero esto reduce la seguridad.

¿Cómo limpio Keychain al cerrar sesión del usuario?

Llame a SecItemDelete con un query que contenga kSecClass para cada tipo de datos. Para una limpieza completa de todos los elementos de la aplicación, ejecute: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Repita para kSecClassKey, kSecClassCertificate y kSecClassInternetPassword.

¿Cuál es la diferencia entre kSecAttrAccessible y kSecAttrAccessControl?

kSecAttrAccessible determina cuándo los datos están disponibles (al desbloquear, después del primer desbloqueo, etc.). kSecAttrAccessControl determina quién puede acceder (biometría, código de acceso, cualquier autenticación). Se combinan: primero Protection Class, luego ACL. Por ejemplo, los datos solo están disponibles al desbloquear Y solo después de Face ID.

¿Por qué SecItemCopyMatching devuelve errSecItemNotFound?

Razones: el elemento nunca se guardó, el elemento se eliminó al quitar el código de acceso (si se usó kSecAttrAccessibleWhenPasscodeSet), la aplicación se reinstaló (Keychain persiste pero no se restaura desde la copia de seguridad en un dispositivo nuevo), el Access Group o el identificador del equipo de desarrollador cambiaron. Verifique kSecAttrService y kSecAttrAccount.

¿Cómo verifico si el dispositivo admite biometría en Keychain?

Use LAContext de LocalAuthentication: llame a context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil). Si devuelve true, el dispositivo admite Touch ID o Face ID. Para la ACL de Keychain, use el indicador biometryCurrentSet (solo datos biométricos actuales) o biometryAny (cualquier registro anterior).

Resumen

  • iOS Keychain es un almacenamiento protegido por hardware para secretos con cifrado a través de Secure Enclave y control de acceso mediante ACL.
  • Security framework proporciona las funciones SecItemAdd, SecItemCopyMatching, SecItemUpdate y SecItemDelete para trabajar con elementos.
  • Protection Class (kSecAttrAccessible) se elige según el escenario: WhenUnlockedThisDeviceOnly es el estándar para tokens.
  • ACL con biometría (kSecAttrAccessControl) agrega un requisito de Face ID o Touch ID para cada operación de lectura.
  • kSecClassGenericPassword cubre el 95% de los casos: almacenamiento de tokens, claves de API, códigos PIN y notas.
  • ThisDeviceOnly evita la copia de secretos a iCloud Backup, obligatorio para tokens de autenticación.
  • Manejo correcto de OSStatus y pruebas en un dispositivo real son prácticas esenciales para un uso fiable de Keychain.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también