38 votes

iOS Trousseau de Services: seules les valeurs spécifiques autorisés pour les kSecAttrGeneric Clé?

Je suis en train d'utiliser le KeychainWrapper classe fournies dans cet Apple exemple de code: http://developer.apple.com/library/ios/#samplecode/GenericKeychain/

Dans l'exemple d'application, la classe a cette méthode init qui commence ainsi:

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;
{
    if (self = [super init])
    {
        // Begin Keychain search setup. The genericPasswordQuery leverages the special user
        // defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain
        // items which may be included by the same application.
        genericPasswordQuery = [[NSMutableDictionary alloc] init];

        [genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
        [genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];

Dans l'exemple d'application, il utilise deux valeurs de l'identificateur de chaîne. "Mot de passe" et "Numéro de Compte". Lors de l'implémentation de la classe dans mon code, j'ai utilisé quelques identificateurs et le code ne fonctionne pas. L'appel à SecItemAdd() a échoué. Après quelques tests, il semble que l'utilisation de valeurs autres que "Mot de passe" et "Numéro de Compte" pour l'identifiant ne fonctionne pas.

Personne ne sait quelles sont les valeurs sont autorisés et/ou si il est possible d'avoir personnalisé les identifiants de votre trousseau d'articles?

61voto

Simon Goldeen Points 6663

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!

10voto

nycynik Points 3069

Même que ci-dessus, mais il travaille pour l'ARC. Merci simon

- (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:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

        [genericPasswordQuery setObject:account forKey:(__bridge id)kSecAttrAccount];
        [genericPasswordQuery setObject:service forKey:(__bridge 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:(__bridge id)kSecAttrAccessGroup];
#endif
        }

        // Use the proper search constants, return only the attributes of the first match.
        [genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
        [genericPasswordQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];

        NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];

        CFMutableDictionaryRef outDictionary = NULL;

        if (! SecItemCopyMatching((__bridge 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:(__bridge id)kSecAttrAccount];
            [keychainItemData setObject:service forKey:(__bridge 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:(__bridge id)kSecAttrAccessGroup];
#endif
            }
        }
        else
        {
            // load the saved data from Keychain.
            keychainItemData = [self secItemFormatToDictionary:(__bridge NSDictionary *)outDictionary];
        }

        if(outDictionary) CFRelease(outDictionary);
    }

    return self;
}

1voto

Zenuka Points 1163

Simon quasi fixe mon problème car après le changement de la KeychainItemWrapper.m, j'ai eu des problèmes à obtenir et les données de réglage et de le trousseau de clés. Ainsi, après l'ajout de cette à la KeychainItemWrapper.m, je l'ai utilisé pour obtenir et stocker des éléments:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithAccount:@"Identfier" service:@"AppName" accessGroup:nil];
[keychainItem setObject:@"some value" forKey:(__bridge id)kSecAttrGeneric];
NSString *value = [keychainItem objectForKey: (__bridge id)kSecAttrGeneric];

Parce qu' [keychainItem objectForKey: (__bridge id)kSecAttrService] est de retour le Compte (dans cet exemple, @"Identifier") ce qui est logique, mais il m'a fallu un certain temps avant de réaliser que j'nécessaires à l'utilisation de kSecAttrGeneric pour récupérer des données à partir de l'emballage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X