Vale, he encontrado la solución en esta entrada del blog Keychain duplicate item when adding password
Para resumir, el problema es que la aplicación de ejemplo GenericKeychain utiliza el valor almacenado en la clave kSecAttrGeneric como el identificador para el elemento llavero cuando en realidad que no es lo que utiliza la API para determinar un elemento único de llavero. Las claves que necesita establecer con valores únicos son la clave kSecAttrAccount y/o la clave kSecAttrService.
Puede volver a escribir la initilizer de KeychainItemWrapper por lo que no es necesario cambiar cualquier otro código cambiando estas líneas:
Cambio:
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];
a:
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrAccount];
y cambio:
[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];
a:
[keychainItemData setObject:identifier forKey:(id)kSecAttrAccount];
O bien, puede hacer lo que hice y escribir un nuevo initilizer que toma tanto de las claves de identificación:
Editar: Para las personas que utilizan ARC (que debe ser hoy en día) comprobar nycynik's answer para toda la notación puente correcta
- (id)initWithAccount:(NSString *)account service:(NSString *)service accessGroup:(NSString *) accessGroup;
{
if (self = [super init])
{
NSAssert(account != nil || service != nil, @"Both account and service are nil. Must specifiy at least one.");
// Begin Keychain search setup. The genericPasswordQuery the attributes kSecAttrAccount and
// kSecAttrService are used as unique identifiers differentiating keychain items from one another
genericPasswordQuery = [[NSMutableDictionary alloc] init];
[genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[genericPasswordQuery setObject:account forKey:(id)kSecAttrAccount];
[genericPasswordQuery setObject:service forKey:(id)kSecAttrService];
// The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
// Use the proper search constants, return only the attributes of the first match.
[genericPasswordQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
NSMutableDictionary *outDictionary = nil;
if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
{
// Stick these default values into keychain item if nothing found.
[self resetKeychainItem];
//Adding the account and service identifiers to the keychain
[keychainItemData setObject:account forKey:(id)kSecAttrAccount];
[keychainItemData setObject:service forKey:(id)kSecAttrService];
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
}
else
{
// load the saved data from Keychain.
self.keychainItemData = [self secItemFormatToDictionary:outDictionary];
}
[outDictionary release];
}
return self;
}
Espero que esto ayude a alguien más a salir!
Pregunta relacionada: http: // stackoverflow.com/questions/11614047/what-makes-a-keychain-item-unique-in-ios –
FWIW, archivé un Radar con Apple sobre este problema con su código de muestra. Vea http://www.openradar.me/13472204 si quiere engañarlo. –