Keychain no iOS: o que é, arquitetura e trabalho com segredos

Autor: IT Sectr Publicado: 2026-04-04 Tempo de leitura: 9 min

Keychain é um armazenamento seguro no iOS projetado para guardar senhas, chaves criptográficas, certificados e notas confidenciais de forma segura. De acordo com Apple Security Documentation (2025), o Keychain usa criptografia por hardware através do Secure Enclave em todos os dispositivos com chip A7 e posteriores. Compreender a arquitetura do iOS Keychain é essencial para todo desenvolvedor armazenar corretamente os tokens e segredos da aplicação.

Principais pontos

  • iOS Keychain é um banco de dados SQLite criptografado para armazenar segredos com proteção por hardware através do Secure Enclave.
  • Protection Class determina quando os dados estão disponíveis: quando o dispositivo está desbloqueado, após o primeiro desbloqueio ou sempre.
  • Access Control List (ACL) é um mecanismo para restringir o acesso a itens do Keychain, incluindo autenticação biométrica.
  • SecItemAdd e SecItemCopyMatching são as principais APIs do Security framework para escrever e ler itens.
  • kSecAttrSynchronizable é o sinalizador que permite a sincronização do Keychain via iCloud para acesso em todos os dispositivos do usuário.

O que é Keychain no iOS?

iOS Keychain é um mecanismo seguro para armazenar dados confidenciais, incorporado ao sistema operacional da Apple. Diferente do UserDefaults ou arquivos comuns, o Keychain criptografa todos os itens em nível de hardware e fornece controle de acesso refinado baseado em políticas de segurança.

O Keychain foi introduzido no iOS 2.0 e desde então passou por mudanças significativas: no iOS 7 foi adicionado suporte a chaves de hardware através do Secure Enclave, no iOS 9 foi introduzido o compartilhamento de Keychain entre aplicativos via Access Groups, no iOS 13 foi adicionado suporte a vinculação biométrica através do LAContext. De acordo com a Apple WWDC Session (2024), mais de 90% dos aplicativos iOS no top 100 da App Store usam Keychain para armazenar tokens de autenticação.

Arquiteturalmente, o Keychain é um banco de dados SQLite criptografado localizado fora do sandbox do aplicativo. Cada item (SecItem) é criptografado com uma chave separada que, por sua vez, é protegida pela chave de hardware Secure Enclave. O serviço de sistema Securityd gerencia o acesso ao Keychain com base nos entitlements do aplicativo e na classe de proteção solicitada.

Uma vantagem importante do Keychain sobre outros métodos de armazenamento: os dados são automaticamente criptografados e descriptografados pelo SO. O desenvolvedor não precisa implementar criptografia manualmente - basta chamar SecItemAdd com os parâmetros corretos. O iOS garante que outros aplicativos não podem ler os dados do Keychain (quando Access Groups estão configurados corretamente).

Arquitetura do Keychain

A arquitetura do Keychain inclui vários níveis: físico (Secure Enclave), sistema (Security.framework), aplicação (API SecItem*) e lógico (Access Groups, Protection Classes). Compreender cada nível ajuda a projetar corretamente o armazenamento de segredos.

SecItemAdd e SecItemCopyMatching

A principal API para trabalhar com Keychain são as funções do Security framework: SecItemAdd para adicionar, SecItemCopyMatching para ler, SecItemUpdate para atualizar e SecItemDelete para excluir. Cada função recebe um dicionário query que descreve os atributos do item a ser encontrado ou salvo.

Atributos chave da query: kSecClass - tipo de item (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount - identificador único dentro da classe, kSecValueData - dados salvos (Data), kSecAttrAccessible - classe de proteção. SecItemCopyMatching com o sinalizador kSecReturnData retorna os dados do item, com kSecMatchLimit - a quantidade de resultados.

Importante: todas as funções retornam um OSStatus. Uma operação bem-sucedida retorna errSecSuccess (0). Erros: errSecItemNotFound (-25300) - item não encontrado, errSecDuplicateItem (-25299) - item já existe, errSecAuthFailed (-25293) - autenticação biométrica falhou. O desenvolvedor deve tratar cada status corretamente.

Classes de proteção (Protection Class)

Protection Class é o atributo kSecAttrAccessible que determina quando os dados no Keychain estão disponíveis para leitura. O iOS suporta seis classes de proteção com diferentes níveis de disponibilidade e segurança.

A classe recomendada para a maioria dos cenários é kSecAttrAccessibleWhenUnlockedThisDeviceOnly: os dados só estão disponíveis quando o dispositivo está desbloqueado e não são copiados para o iCloud Backup. Para dados que devem estar disponíveis após a reinicialização (mas apenas após o primeiro desbloqueio), use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Para dados críticos que exigem autenticação biométrica a cada acesso, combine kSecAttrAccessibleWhenUnlockedThisDeviceOnly com uma ACL que requer biometria.

As classes sem o sufixo ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) permitem a cópia para o iCloud Backup. Isso é conveniente para o usuário, mas reduz a segurança - os dados podem ser restaurados do backup. Para tokens de autenticação, use sempre ThisDeviceOnly.

Listas de controle de acesso (ACL)

Access Control List (ACL) é um mecanismo que restringe operações em um item do Keychain com base na autenticação do usuário. A ACL é definida através de SecAccessControlCreateWithFlags e passada para o atributo kSecAttrAccessControl ao salvar um item.

Sinalizadores suportados: kSecAccessControlUserPresence - qualquer autenticação (Face ID, Touch ID ou código de acesso), kSecAccessControlBiometryCurrentSet - apenas biometria (impressões digitais ou rosto atualmente registrados), kSecAccessControlDevicePasscode - apenas código de acesso. A ACL se aplica a cada operação: ler, atualizar e excluir um item também exigem autenticação.

No iOS 15+ apareceu o sinalizador kSecAccessControlWatch - para Apple Watch, que permite autenticação através de um relógio pareado. A ACL pode ser combinada: por exemplo, kSecAccessControlUserPresence ou kSecAccessControlBiometryAny com um código de acesso opcional (orientação .or).

Tipos de dados no Keychain

iOS Keychain suporta quatro classes principais de itens (kSecClass), cada uma projetada para seu próprio tipo de dado. Escolher a classe correta simplifica a organização e a busca de itens.

kSecClassGenericPassword - senha genérica: a classe mais usada. Armazena dados binários arbitrários (Data) com uma chave única (kSecAttrAccount). Adequado para tokens, chaves de API, códigos PIN. Não requer entitlements adicionais para uso.

kSecClassInternetPassword - senha de internet: armazena dados associados a um recurso de rede. Atributos adicionais: kSecAttrServer (domínio do servidor), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. O iOS pode preencher automaticamente essas senhas através do AutoFill.

kSecClassKey - chave criptográfica: para armazenar chaves de criptografia (AES, RSA, EC). A chave é armazenada como SecKeyRef, não como Data. kSecClassCertificate - certificado X.509 para armazenar e verificar certificados digitais. Ambas as classes exigem compreensão de operações criptográficas e configuração correta de atributos.

Na prática, 95% dos casos de uso do Keychain em aplicativos móveis são cobertos por kSecClassGenericPassword para armazenar tokens de autenticação e kSecClassKey para armazenar chaves de criptografia privadas. kSecClassCertificate é raramente usado - geralmente em aplicativos empresariais com PKI próprio.

Exemplos de código: trabalhando com Keychain em Swift

Vamos ver exemplos práticos de trabalho com Keychain em Swift usando o Security framework. Cada exemplo inclui tratamento de erros e configuração correta da Protection Class.

Salvando e lendo um token

Um exemplo básico salva um token de autenticação no Keychain com proteção WhenUnlockedThisDeviceOnly. A chave (kSecAttrAccount) é o identificador do serviço, os dados (kSecValueData) são o token em 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)
}

Salvando com vinculação biométrica

O exemplo demonstra o uso de SecAccessControlCreateWithFlags para vincular uma chave à biometria. Cada acesso ao item exigirá Face ID ou 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 aplicativos

O exemplo mostra a configuração de um Access Group para acesso compartilhado ao Keychain entre aplicativos do mesmo desenvolvedor. Requer o 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)
}

Melhores práticas para trabalhar com Keychain

O uso correto do iOS Keychain requer seguir várias regras importantes que previnem vulnerabilidades comuns e perda de dados.

Use ThisDeviceOnly para todos os segredos de autenticação: kSecAttrAccessibleWhenUnlockedThisDeviceOnly garante que os tokens não vão para o iCloud Backup. Se um invasor obtiver acesso ao backup, os dados do Keychain com este sinalizador não estarão disponíveis. A exceção são dados que devem estar disponíveis em todos os dispositivos do usuário (por exemplo, chaves de criptografia para serviços proprietários), para os quais use kSecAttrAccessibleWhenUnlocked com kSecAttrSynchronizable.

Não armazene senhas em texto puro - armazene hashes ou tokens de sessão. A Apple Security Guide (2025) recomenda nunca salvar a senha do usuário no Keychain em texto claro. Em vez disso, salve o refresh token recebido do servidor após autenticação bem-sucedida via OAuth 2.0. A senha é usada apenas para obter o token e é imediatamente removida da memória.

Trate os erros do Keychain corretamente: cada operação do Keychain retorna um OSStatus que deve ser verificado. Preste atenção especial a errSecItemNotFound (token expirado ou excluído) e errSecAuthFailed (biometria falhou). No primeiro caso, o aplicativo deve solicitar nova autenticação; no segundo, mostrar ao usuário um método alternativo (código de acesso). Nunca ignore o status errSecItemNotFound - isso causará uma falha ao tentar ler nil.

Teste o Keychain em um dispositivo real: o simulador não possui Secure Enclave e não suporta ACLs biométricos. Sempre verifique os cenários: primeiro lançamento, restauração de backup, alteração de senha do dispositivo, exclusão e reinstalação do aplicativo. Em um dispositivo real, o Keychain persiste quando o aplicativo é excluído, mas apenas se o sinalizador kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly não foi usado, que é limpo quando o código de acesso é removido.

Minimize o número de operações com Keychain: cada operação de leitura ou escrita é uma chamada ao serviço de sistema Securityd, que pode bloquear a thread. Armazene em cache os tokens lidos em memória durante a sessão e acesse o Keychain novamente apenas ao reiniciar o aplicativo ou em erro de autenticação (401 do servidor). O iOS bloqueia automaticamente o Keychain quando o dispositivo é bloqueado, portanto, planeje a leitura através de LAContext com uma solicitação biométrica.

Perguntas frequentes

Posso salvar dados no Keychain e lê-los após uma reinicialização?

Sim, use a classe de proteção kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ou kSecAttrAccessibleAfterFirstUnlock. Os dados estarão disponíveis após o primeiro desbloqueio do dispositivo após uma reinicialização. Para acesso automático ao iniciar o aplicativo (sem aguardar o desbloqueio), use kSecAttrAccessibleAlways, mas isso reduz a segurança.

Como limpar o Keychain ao sair do usuário?

Chame SecItemDelete com uma consulta contendo kSecClass para cada tipo de dado. Para limpeza completa de todos os itens do aplicativo, execute: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Repita para kSecClassKey, kSecClassCertificate e kSecClassInternetPassword.

Qual é a diferença entre kSecAttrAccessible e kSecAttrAccessControl?

kSecAttrAccessible determina quando os dados estão disponíveis (ao desbloquear, após o primeiro desbloqueio, etc.). kSecAttrAccessControl determina quem pode acessar (biometria, código de acesso, qualquer autenticação). Eles se combinam: primeiro Protection Class, depois ACL. Por exemplo, os dados só estão disponíveis ao desbloquear E somente após Face ID.

Por que SecItemCopyMatching retorna errSecItemNotFound?

Motivos: o item nunca foi salvo, o item foi excluído ao remover o código de acesso (se kSecAttrAccessibleWhenPasscodeSet foi usado), o aplicativo foi reinstalado (Keychain persiste mas não é restaurado do backup em um novo dispositivo), o Access Group ou identificador da equipe de desenvolvimento mudou. Verifique kSecAttrService e kSecAttrAccount.

Como verificar se o dispositivo suporta biometria no Keychain?

Use LAContext do LocalAuthentication: chame context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil). Se retornar true - o dispositivo suporta Touch ID ou Face ID. Para ACL do Keychain, use o sinalizador biometryCurrentSet (apenas dados biométricos atuais) ou biometryAny (qualquer registro anterior).

Resumo

  • iOS Keychain é um armazenamento protegido por hardware para segredos com criptografia através do Secure Enclave e controle de acesso via ACL.
  • Security framework fornece as funções SecItemAdd, SecItemCopyMatching, SecItemUpdate e SecItemDelete para trabalhar com itens.
  • Protection Class (kSecAttrAccessible) é escolhida com base no cenário: WhenUnlockedThisDeviceOnly é o padrão para tokens.
  • ACL com biometria (kSecAttrAccessControl) adiciona um requisito de Face ID ou Touch ID para cada operação de leitura.
  • kSecClassGenericPassword cobre 95% dos casos - armazenamento de tokens, chaves de API, códigos PIN e notas.
  • ThisDeviceOnly impede a cópia de segredos para o iCloud Backup - obrigatório para tokens de autenticação.
  • Tratamento correto de OSStatus e testes em dispositivo real são práticas essenciais para o uso confiável do Keychain.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também