Pregunta

He estado tratando de usar referencias de llavero persistentes en una aplicación para iPhone. Descubrí que si creara dos elementos de llavero diferentes, obtendría una referencia persistente diferente cada vez (se parecen a 'GenP ....... 1', 'GenP ....... 2', ...) . Sin embargo, los intentos de buscar los elementos por referencia persistente siempre devolvieron el contenido del primer elemento. ¿Por qué debería ser esto? Confirmé que mi código de ahorro de llavero definitivamente estaba creando nuevos elementos en cada caso (en lugar de actualizar los elementos existentes), y no recibía ningún error. Y como digo, Keychain Services está dando una referencia persistente diferente para cada elemento.

He logrado resolver mi problema inmediato buscando elementos de llavero mediante atributo en lugar de referencias persistentes, pero sería más fácil usar referencias persistentes, por lo que agradecería resolver este problema.

Aquí está mi código:

- (NSString *)keychainItemWithName: (NSString *)name {
    NSString *path = [GLApplicationSupportFolder()
                      stringByAppendingPathComponent: name];
    NSData *persistentRef = [NSData dataWithContentsOfFile: path];
    if (!persistentRef) {
        NSLog(@"no persistent reference for name: %@", name);
        return nil;
    }
    NSArray *refs = [NSArray arrayWithObject: persistentRef];
    //get the data
    CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
                                                              0,
                                                              &kCFTypeDictionaryKeyCallBacks,
                                                              &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(params, kSecMatchItemList, refs);
    CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(params, kSecReturnData, kCFBooleanTrue);
    CFDataRef item = NULL;
    OSStatus result = SecItemCopyMatching(params, (CFTypeRef *)&item);
    CFRelease(params);
    if (result != errSecSuccess) {
        NSLog(@"error %d retrieving keychain reference for name: %@", result, name);
        return nil;
    }
    NSString *token = [[NSString alloc] initWithData: (NSData *)item
                                            encoding: NSUTF8StringEncoding];
    CFRelease(item);
    return [token autorelease];
}

- (void)setKeychainItem: (NSString *)newToken forName: (NSString *)name {
    NSData *tokenData = [newToken dataUsingEncoding: NSUTF8StringEncoding];
    //firstly, find out whether the item already exists
    NSDictionary *searchAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      name, kSecAttrAccount,
                                      kCFBooleanTrue, kSecReturnAttributes,
                                      nil];
    NSDictionary *foundAttrs = nil;
    OSStatus searchResult = SecItemCopyMatching((CFDictionaryRef)searchAttributes,
                                                (CFTypeRef *)&foundAttrs);
    if (noErr == searchResult) {
        NSMutableDictionary *toStore = [foundAttrs mutableCopy];
        [toStore setObject: tokenData forKey: (id)kSecValueData];
        OSStatus result = SecItemUpdate((CFDictionaryRef)foundAttrs,
                                        (CFDictionaryRef)toStore);
        if (result != errSecSuccess) {
            NSLog(@"error %d updating keychain", result);
        }
        [toStore release];
        return;
    }
    //need to create the item.
    CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
                                                              0,
                                                              &kCFTypeDictionaryKeyCallBacks,
                                                              &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(params, kSecAttrAccount, name);
    CFDictionaryAddValue(params, kSecReturnPersistentRef, kCFBooleanTrue);
    CFDictionaryAddValue(params, kSecValueData, tokenData);
    NSData *persistentRef = nil;
    OSStatus result = SecItemAdd(params, (CFTypeRef *)&persistentRef);
    CFRelease(params);
    if (result != errSecSuccess) {
        NSLog(@"error %d from keychain services", result);
        return;
    }
    NSString *path = [GLApplicationSupportFolder()
                      stringByAppendingPathComponent: name];
    [persistentRef writeToFile: path atomically: NO];
    [persistentRef release];
}
¿Fue útil?

Solución

Resulta que usar la KsecMatchItemList no parece funcionar en absoluto.

Hice el mío así:

NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
                     (id)kSecClassGenericPassword, kSecClass,
                     persistentRef, (id)kSecValuePersistentRef,
                     (id)kCFBooleanTrue, kSecReturnAttributes,
                     (id)kCFBooleanTrue, kSecReturnData,
                     nil];
NSDictionary *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query,
                                    (CFTypeRef*)&result);

que devolvieron los atributos y datos para la referencia persistente. La documentación en el encabezado sobre la conversión de una "referencia persistente" en una "referencia estándar" no tiene ningún sentido. Espero que esto ayude.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top