Ошибка сохранения в связке ключей с iphone SDK - PullRequest
27 голосов
/ 30 ноября 2010

Я использую оболочку Apple для цепочки для ключей и пытаюсь сохранить на ней элемент (работает в симуляторе, ios 4.1).

До этого у меня не было опыта работы с цепочкой для ключей.

Я получаю эту ошибку:

Не удалось добавить элемент цепочки для ключей.Ошибка - 25299

В строке 304 KeychainItemWrapper.m:

// No previous item found; add the new one.
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @"Couldn't add the Keychain Item." );

Вот как я сохраняю:

- (void) saveKey:(NSString *)key value:(NSString *)value {
    KeychainItemWrapper *keyItem = [[KeychainItemWrapper alloc] initWithIdentifier:key accessGroup:nil];
    [keyItem setObject:value forKey:(id)kSecValueData];
    [keyItem release];
}

И это значениячто API пытается сохранить:

<CFBasicHash 0x7231f60 [0x320d380]>{type = mutable dict, count = 5,
entries =>
2 : <CFString 0x2e6eb98 [0x320d380]>{contents = "labl"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
3 : <CFString 0x2e6efb8 [0x320d380]>{contents = "v_Data"} = <CFString 0x727de60 [0x320d380]>{contents = "dit8"}
4 : <CFString 0x2e6ebc8 [0x320d380]>{contents = "acct"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
5 : <CFString 0x2e6eb58 [0x320d380]>{contents = "desc"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
6 : <CFString 0x2e6ebe8 [0x320d380]>{contents = "gena"} = <CFString 0x2ffd08 [0x320d380]>{contents = "userCode"}
}

Ответы [ 7 ]

60 голосов
/ 23 сентября 2011

Я знаю, что это было несколько месяцев назад, но у меня была та же самая проблема, и это было больно, поэтому я решил поделиться.Я решил это, добавив следующую строку:

[self.keychainItemWrapper setObject:@"MY_APP_CREDENTIALS" forKey:(id)kSecAttrService];
//@"MY_APP_CREDENTIALS" can be any string.

Я нашел эту запись в блоге очень полезной: «В терминах базы данных вы можете подумать, что они являются уникальным индексом для двух атрибутов kSecAttrAccount, kSecAttrService, требующих комбинации этихдва атрибута, которые должны быть уникальными для каждой записи в цепочке для ключей. "(из http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html).

Кроме того, в примере проекта Apple, использующего этот код, они создают экземпляр KeychainItemWrapper в делегате приложения. Я не знаю, если это необходимо, но мне нравится следовать их примерам настолько близко, насколько это возможно:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
//there will be some standard code here.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MY_APP_CREDENTIALS" accessGroup:nil];
self.keychainWrapper = wrapper;
[self.keychainWrapper setObject:@"MYOBJECT" forKey:(id)kSecAttrService];
[wrapper release];
}

Я думаю, что это ошибка в коде оболочки. Логика в основном говорит: «Эта запись уже существует? Нет, это не так. Хорошо, я добавлю ее. Ой, вы можетене добавляйте его, потому что он уже есть. "

Вам также может понадобиться установить kSecAttrAccount; я никогда не пробовал его, не устанавливая также это значение, поскольку он предназначен для сохранения имени пользователя, которое идет с паролем:

[self.wrapper setObject:txtUserName.text forKey:(id)kSecAttrAccount];   
9 голосов
/ 08 апреля 2011

Согласно документации , ошибка -25299, которую вы получаете, является "errSecDuplicateItem", означая, что элемент, который вы пытаетесь добавить, уже существует.Глядя на связанный код для KeychainItemWrapper, я могу предположить, что вызов SecItemCopyMatching завершается с ошибкой, отличной от errSecItemNotFound (–25300).

6 голосов
/ 11 апреля 2011

Вы можете легко сохранять и возвращать значения с помощью цепочки для ключей, используя SFHFKeychainUtils от Buzz Andersen.

  1. Загрузите и скопируйте в свой проект SFHFKeychainUtils.h и .m
  2. Добавить Security.framework в папку Framework
  3. Убедитесь, что эти файлы добавлены к вашей цели
  4. Импорт SFHFKeychainUtils.h, где вы хотите его использовать

Это небольшой пример использования этой библиотеки.

// To store data
NSError *error = nil;
[SFHFKeychainUtils storeUsername:username andPassword:password forServiceName:kStoredData updateExisting:YES error:&error];

// To retrieve data
NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:kStoredData error:&error];

// To delete data from keychain
[SFHFKeychainUtils deleteItemForUsername:username andServiceName:kStoredData error:&error];
3 голосов
/ 08 апреля 2011

Брелок - это полная боль. Вы должны использовать Buzz Andersen STUtils вместо этого в качестве оболочки Это существенно облегчит вашу жизнь. У меня никогда не было с этим проблем.

1 голос
/ 21 мая 2015

Для меня решение состояло в том, что я создал KeychainItemWrapper «синглтон» и использовал его во всем приложении. (На самом деле, в моем случае у меня был словарь-одиночка, полный KeychainItemWrapper -s, потому что я использую более одного.)

Это решило проблему, когда я нашел путь к коду, который фактически сказал: «Этот элемент существует в цепочке для ключей? Нет? Затем добавьте его. К сожалению! NSAssert(), что я пытаюсь добавить уже существующий элемент (Ошибка -25299) "

Хотя я не уверен, я подозреваю, что проблема связана с синхронизацией цепочки для ключей. У меня были подобные проблемы с NSUserDefaults, когда я пишу в NSUD, затем, в другом месте кода, получаю standardUserDefaults и считываю их, а обновление еще не произошло (потому что я не сделал [ud synchronize], пока.)

В коде моя рутина выглядит так:

+ (KeychainItemWrapper*) keyChainWrapperForKeyID: (NSString*) keyID
{
    static dispatch_once_t onceToken = 0;
    static NSMutableDictionary *rfcuKeyChains = nil;
    dispatch_once(&onceToken, ^{
        rfcuKeyChains = [NSMutableDictionary new];
    });

    KeychainItemWrapper *keychain = nil;
    @synchronized (rfcuKeyChains)
    {
        keychain = [rfcuKeyChains objectForKey: keyID];
        if (keychain == nil)
        {
            keychain = [[KeychainItemWrapper alloc] initWithIdentifier: keyID accessGroup: nil];
            [rfcuKeyChains setObject: keychain forKey: keyID];
        }
    }

    return keychain;
}

И я использую это так:

KeychainItemWrapper *keychain = [RFCUtils keyChainWrapperForKeyID: keyID];
NSString *firstLaunch = [keychain objectForKey: (__bridge id)(kSecAttrAccount)];
if (firstLaunch == nil)
{
    [keychain setObject: MY_APP_KEY forKey: (__bridge id)(kSecAttrAccount)];
}

(и т. Д. Аналогичные звонки в других местах.)

0 голосов
/ 10 октября 2016

Я попробовал все решения, слушайте выше, но у меня ничего не получалось.Он работал только на реальном устройстве, но не на симуляторе.

Мое решение запустить его на симуляторе состояло в том, чтобы включить «Право доступа к ключевой цепи».

Право поделиться ключевой цепью

0 голосов
/ 10 июля 2013

У меня тоже была эта проблема, и я решил ее благодаря ответу на акцептор и дополнительной ссылке на useyourloaf.

У меня была интересная проблема, мне нужно было сохранить только одно значение и я решил сохранить его в поле kSecValueData . Это потому, что я видел другие сообщения об использовании цепочки для ключей и начал свою собственную реализацию прежде, чем обратиться к KeychainItemWrapper. Это вызвало следующую проблему: на первом устройстве, которое я тестировал (iPad 1st gen), я получил ошибку в writeToKeychain. Я сменил устройство (также ipad 1st gen), и оно заработало! Возвращаясь к первому устройству, оно все еще не работало.

Итак, я знал, что ранее я сделал что-то не так в цепочке для ключей устройства и не мог легко это исправить. Коды ошибок, которые я получал, были: -25300 на SecItemCopyMatching writeToKeychain (элемент не найден) и сразу после -25299 на SecItemAdd. (дубликат)

С этим вопросом все это имело смысл: у устройства есть ключ, который соответствует любому новому ключу, но KeychainItemWrapper не может удалить его, но ключ не может быть получен. Как только я добавил то же значение в поле kSecAttrAccount, оно начало работать.

Короче говоря, для других пользователей, имеющих эту проблему, ваша проблема может выглядеть иначе, но обратите внимание на детали. Если у вас есть -25300 (элемент не найден), а затем -25299 (дубликат элемента); убедитесь, что вы устанавливаете поле, которое определяет уникальность вашего элемента цепочки для ключей. Если это не работает на одном устройстве, попробуйте другое, если можете, возможно, вы сможете изолировать проблему на одном устройстве. Брелок для ключей Apple. Коды ошибок: http://developer.apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898-CH5g-CJBEABHG (поиск кодов результатов)

...