secItemCopyMatching retorna dados nulo
Primeiramente, eu assisti a sessão da WWDC 2013 sobre proteção de segredos com o chaveiro. Eu quero fazer uma loja básica de senhas. Assisti todo o vídeo, mas encontrei o que precisava nos primeiros 10 minutos do vídeo. Parece simples, mas não entendo completamente como funciona a codificação e recuperação de dados.
PROBLEMA: depois de secItemCopyMatching, eu verifico meu objeto NSData para ter certeza de que não é nulo antes de convertê-lo em um NSString. O problema é que é sempre nulo. Abaixo está como eu estou salvando a entrada de chaves ou atualização, seguido por como eu estou recuperando. Qualquer ajuda e explicação seria muito apreciada.
UPDATE (EDITED): Fruity Geek, obrigado pela resposta. Eu atualizei meu código abaixo usando __bridge. Meu problema agora resume-se a, estou armazenando e recuperando a senha corretamente? Eu tenho ambos errado ou apenas um ou outro? Minha instância NSData é sempre nula. Eu estou verificando códigos de retorno e meu SecItemAdd e SecItemUpdate (quando a entrada keychaing existe) estão funcionando corretamente. Não consigo recuperar o valor da string dos dados (senha) armazenados para compará-los com a senha digitada pelo usuário. Aprecie os rapazes e moças da ajuda. Aqui está o que estou fazendo agora:
ATUALIZAÇÃO # 2: (Editado com as respostas do Fruity Geek e versão final de trabalho. Minhas edições incluem apenas alterações no código abaixo.)
Definir entrada de chaveiro:
NSData *secret = [_backupPassword dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
(__bridge id)kSecValueData: secret,
};
OSStatus status =
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
if (status == errSecDuplicateItem) {
// this item exists in the keychain already, update it
query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
};
NSDictionary *changes = @{
(__bridge id)kSecValueData: secret,
};
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes);
}
Recuperar senha do keychain:
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
(__bridge id)kSecReturnData: @YES,
};
NSData *data = NULL;
CFTypeRef dataTypeRef = (__bridge CFTypeRef)data;
OSStatus status =
SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);
NSData *data = (__bridge NSData *)dataTypeRef;
NSString *passcode = @"none";
if (status == errSecSuccess) {
// we found a keychain entry, set the passcode
if (data)
passcode = [NSString stringWithUTF8String:[data bytes]];
}
twServiceName e twAccountName são NSStrings estáticos.
Como eu disse, não sei bem o que estou fazendo com __bridge ou CFTypeRef. Eu olhei através de documentos de maçãs, numerosos posts aqui e outros sites, mas chaves e esses termos são novos para mim e eu ainda estou tentando descobrir isso. Espero que alguém aqui possa apontar meu erro e me ajudar a entender. Obrigado antecipadamente pela ajuda.
iOS 7 / Xcode 5