No momento, estou armazenando o nome de usuário (e-mail) e um hash com sal do e-mail e senha no iOS KeyChain. Estou usando a versão ARC'ified encontrada aqui .
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
[wrapper setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
[wrapper setObject:email forKey:(__bridge id)kSecAttrAccount];
[wrapper setObject:token forKey:(__bridge id)kSecValueData];
Tudo isso funciona bem quando preciso puxar o token para minhas chamadas de rede enquanto o aplicativo está ativo. Ele funciona para fazer login a partir de uma inicialização limpa, bem como para todas as chamadas de rede. O problema começa quando o aplicativo está em segundo plano.
Lembre-se de que isso só acontece esporadicamente e ainda preciso definir uma versão ou dispositivo específico do iOS.
O usuário tropeça em um local (monitoramento de região) e eu quero atualizar o servidor com seu status. Tento retirar o token do chaveiro, da mesma forma que faço para todas as outras chamadas de rede, e atualizo o status. Mas, para alguns usuários, o valor é nulo. Sem ele, não consigo atualizar as coisas da rede. Por que isso funcionaria para a maioria, mas não para uma pequena porcentagem?
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];
Voltei para a versão não ARC do keychainwrapper, mas ainda obtenho os mesmos resultados. Eu gostaria de receber qualquer feedback sobre isso. É apenas uma pequena parte dos meus usuários, mas é um problema que eu gostaria de corrigir e não me preocuparia. Desde já, obrigado.
Além disso, todo o meu trabalho em segundo plano é configurado em uma backgroundTask para evitar que as coisas se esgotem. Não estou tendo problemas com o trabalho em torno do chaveiro, mas não deixo as coisas avançarem até que meu token seja preenchido.
EDITAR Eu descobri meu problema com as chaves que não recuperam valores do fundo. Vou postar a resposta abaixo e aceitá-la, pois sinto que essa pergunta pode ser valiosa para outras pessoas mais tarde.
fonte
…AccessibleAlways
se possível, ou armazene um token que forneça apenas privilégios limitados (por exemplo, um token que permite que você leia novos itens de feed, mas não publique). Você está renunciando explicitamente a um nível de criptografia ao fazer isso. Se seu aplicativo pode esperar até o primeiro desbloqueio, talvez seja melhor usar…AfterFirstUnlock
e direcionar seus usuários para desbloquear seus dispositivos primeiro.Use em
kSecAttrAccessibleAfterFirstUnlock
vez dekSecAttrAccessibleAlways
.Da documentação da Apple :
fonte
kSecAttrAccessibleAlways
já está obsoletaNo meu caso, watchOS2 acessa os dados das chaves no lado do iOS.
No início, kSecAttrAccessibleWhenUnlockedThisDeviceOnly é usado. Posso ler os dados independentemente de o iPhone estar bloqueado ou não. É muito confuso para mim receber um erro quando o relógio tentar acessar o keychain:: SecTrustEvaluate [leaf IssuerCommonName SubjectCommonName]
E em alguns casos ele se tornará:: SecOSStatusWith erro: [- 25308] Domínio de erro = NSOSStatusErrorDomain Code = -25308 "ks_crypt: e00002e2 falhou ao 'oe' item (classe 6, bolsa: 0) Acesso ao item tentado enquanto o keychain estava bloqueado. " UserInfo = {NSDescription = ks_crypt: e00002e2 falhou ao 'oe' item (classe 6, bolsa: 0) Acesso ao item tentado enquanto o chaveiro está bloqueado.}
Vou atualizar minha resposta se receber mais informações.
fonte
Isso pode acontecer devido à política de proteção de dados da Apple, que em algum nível é obscura da perspectiva dos desenvolvedores. A solução alternativa é quando o aplicativo é lançado, verifica se o keychain está acessível ou não, se não estiver acessível, você pode encerrar o aplicativo (com pop-up adequado), dependendo dos tipos de aplicativo.
+(BOOL) isKeychainAccessible { NSString *keychainTestKey = @"keychainTestKey"; NSString *keychainTestValue = @"keychainTestValue"; [self createKeychainValue:keychainTestValue forIdentifier:keychainTestKey]; NSString *loadedValue = [self keychainStringFromMatchingIdentifier:keychainTestKey]; [self deleteItemFromKeychainWithIdentifier:keychainTestKey]; return ([keychainTestValue isEqualToString: loadedValue]); }
fonte