ver llavero guardadas entrar descargar cómo contraseñas como acceder ios security password-protection keychain

ios - guardadas - llavero icloud ver contraseñas wifi



Llavero: artículo reportado como errSecItemNotFound, pero recibe errSecDuplicateItem al agregarlo (1)

La clave única para kSecClassGenericPassword se compone de;

kSecAttrAccount kSecAttrService

Para verificar su existencia, consulte el almacén de llaveros solo con estos atributos (incluido el indicador kSecReturnAttributes ).

Incluir kSecAttrLabel y kSecAttrAccessible excluirá cualquier artículo existente con la misma clave única, pero con atributos diferentes.

Una vez que haya confirmado su (no) existencia, agregue los atributos adicionales y Agregar o Actualizar.

Este problema me ha estado molestando por un tiempo, y espero que alguien tenga una idea de la causa de esto. Esencialmente, tengo un pequeño porcentaje de usuarios que no pueden guardar / actualizar elementos en el llavero. El flujo de control problemático es el siguiente:

  1. Verificamos la existencia del artículo utilizando SecItemCopyMatching . Esto devuelve el código de error errSecItemNotFound

  2. Luego intentamos agregar el elemento a través de SecItemAdd , pero esto luego devuelve errSecDuplicateItem .

Debido a esto, tenemos algunos usuarios que no pueden actualizar un subconjunto de elementos de llavero en absoluto, lo que requiere que restauren su dispositivo para borrar el llavero. Esto es obviamente una solución inaceptable. Parecía funcionar para ellos antes, pero ahora han entrado en este ciclo no actualizable.

Después de investigar, he visto que los problemas relacionados con la consulta de búsqueda utilizados en SecItemCopyMatching no son lo suficientemente específicos, pero mi código utiliza una consulta de búsqueda común siempre que sea posible.

+ (NSMutableDictionary*)queryForUser:(NSString*)user key:(NSString*)key { if (!key || !user) { return nil; } NSString* bundleId = [[NSBundle mainBundle] bundleIdentifier]; NSString* prefixedKey = [NSString stringWithFormat:@"%@.%@", bundleId, key]; NSMutableDictionary* query = [NSMutableDictionary dictionary]; [query addEntriesFromDictionary:@{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword}]; [query addEntriesFromDictionary:@{(__bridge id)kSecAttrAccount : user}]; [query addEntriesFromDictionary:@{(__bridge id)kSecAttrService : prefixedKey}]; [query addEntriesFromDictionary:@{(__bridge id)kSecAttrLabel : prefixedKey}]; [query addEntriesFromDictionary:@{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly}]; return query; }

El código para hacer la actualización / adición es el siguiente (perdón por la verbosidad):

// Setup the search query, to return the *attributes* of the found item (for use in SecItemUpdate) NSMutableDictionary* query = [self queryForUser:username key:key]; [query addEntriesFromDictionary:@{(__bridge id)kSecReturnAttributes : (__bridge id)kCFBooleanTrue}]; // Prep the dictionary we''ll use to update/add the new value NSDictionary* updateValues = @{(__bridge id) kSecValueData : [value dataUsingEncoding:NSUTF8StringEncoding]}; // Copy what we (may) already have CFDictionaryRef resultData = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&resultData); // If it already exists, update it if (status == noErr) { // Create a new query with the found attributes NSMutableDictionary* updateQuery = [NSMutableDictionary dictionaryWithDictionary:(__bridge NSDictionary*)resultData]; [updateQuery addEntriesFromDictionary:@{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword}]; // Update the item in the keychain status = SecItemUpdate((__bridge CFDictionaryRef)updateQuery, (__bridge CFDictionaryRef)updateValues); if (status != noErr) { // Update failed, I''ve not seen this case occur as of yet } } else { // Add the value we want as part of our original search query, and add it to the keychain [query addEntriesFromDictionary:updateValues]; [query removeObjectForKey:(__bridge id)kSecReturnAttributes]; status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); if (status != noErr) { // Addition failed, this is where I''m seeing errSecDuplicateItem } }

Intentamos usar SecItemDelete lugar de verificar / actualizar, pero esto también devolvió errSecItemNotFound con SecItemAdd fallando inmediatamente después. El código de eliminación es:

+ (BOOL)deleteItemForUser:(NSString *)username withKey:(NSString *)itemKey { if (!username || !itemKey) { return NO; } NSString * bundleId = [[NSBundle mainBundle] bundleIdentifier]; NSString * prefixedItemKey = [NSString stringWithFormat:@"%@.%@", bundleId, itemKey]; NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)kSecClassGenericPassword, kSecClass, username, kSecAttrAccount, prefixedItemKey, kSecAttrService, nil]; OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); if (status != noErr) { // Failed deletion, returning errSecItemNotFound } return (status == noErr); }

Si bien hemos definido 2 grupos de acceso de llavero para la aplicación, los elementos de llavero afectados no tienen un grupo de acceso asignado como un atributo (que, según la documentación, significa que se realizarán búsquedas para todos los grupos de acceso). Todavía no he visto ningún otro código de error que no sea errSecItemNotFound y errSecDuplicateItem .

El hecho de que solo un pequeño grupo de usuarios entre en esta condición realmente me confunde. ¿Hay alguna otra consideración que deba tener en cuenta con respecto al llavero que podría estar causando esto, con respecto a los subprocesos múltiples, el lavado, el acceso al fondo, etc.?

Ayuda muy apreciada. Prefiero seguir usando la API de servicios de llavero en lugar de usar una biblioteca de terceros. Me gustaría entender el problema fundamental aquí.