Bon, j'ai trouvé la solution dans ce blog, Keychain de l'article en double lors de l'ajout d'un mot de passe
Pour résumer, le problème est que le GenericKeychain exemple d'application utilise la valeur stockée dans kSecAttrGeneric clés comme identifiant pour le trousseau de l'élément, alors qu'en fait ce n'est pas ce que l'API utilise pour déterminer un unique trousseau élément. Les touches dont vous avez besoin pour mettre avec des valeurs uniques sont le kSecAttrAccount clé et/ou le kSecAttrService clé.
Vous pouvez réécrire le initilizer de KeychainItemWrapper de sorte que vous n'avez pas besoin de modifier tout autre code en changeant ces lignes:
Changement:
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];
pour:
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrAccount];
et le changement:
[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];
pour:
[keychainItemData setObject:identifier forKey:(id)kSecAttrAccount];
Ou, vous pourriez faire ce que j'ai fait et écrire une nouvelle initilizer qui prend à la fois de l'identification de touches:
Edit: Pour les personnes utilisant un ARC (vous devriez l'être aujourd'hui) vérifier nycynik de réponse pour tous les bons de transition notation
- (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;
}
Espérons que cela aide quelqu'un d'autre!