Ошибка сохранения в связке ключей с iphone sdk

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

У меня не было опыта работы с брелоком раньше.

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

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

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

// 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"}
}

person mamcx    schedule 29.11.2010    source источник
comment
Я застрял с этим, поэтому я думаю, что награда - хорошая идея. Тот же код ошибки, но iOS4.3. Но я думаю, что это проблема PEBKAC, а не проблема с SDK.   -  person Matthias Bauch    schedule 08.04.2011
comment
Эта проблема возникла у меня, когда я добавил [keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrService]; в - (void)resetKeychainItem. Затем, когда я читал ответ пользователя 379075 ниже, он позвонил в звонок, что если вы «сбросите» его, вы также должны установить его и v.v.   -  person mrd3650    schedule 13.04.2012


Ответы (7)


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

[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];   
person Dwayne Driskill    schedule 23.09.2011
comment
Работал нормально после добавления оператора MY_APP_CREDENTIALS. Большое спасибо :) - person ilight; 03.09.2012
comment
Я думаю, важно отметить, что при поиске элементов связки ключей вы должны указывать ТОЛЬКО атрибуты учетной записи и службы для поиска. Если вы укажете больше атрибутов, система будет искать совпадения по всем из них, поэтому вы можете ничего не получить, даже если элемент уже существует для этой учетной записи и услуги, поэтому попытка добавить и войти с этой учетной записью и услугой в брелок выйдет из строя. - person jpb; 28.09.2017

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

person Anomie    schedule 08.04.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];
person matteodv    schedule 11.04.2011
comment
Что такое kStoredData в этом контексте? Я получаю ошибку сборки при попытке сделать это. - person Philip Walton; 22.04.2011
comment
В этом случае kStoreData — это определенный ключ... Вы можете использовать любой ключ... В этом примере kStoreData — это ключ для имени службы, такого как @com.company.app.serviceName. Вы можете определить такой ключ в своем файле .h, а затем использовать его для хранения и извлечения ваших данных. - person matteodv; 22.04.2011

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

person Andy McSherry    schedule 07.04.2011
comment
Этот проект github устарел; автор предлагает github.com/ldandersen/STUtils/blob/master/Security в качестве замены. - person Graham Miln; 11.10.2012

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

Это решило проблему, когда я добирался до пути кода, который фактически говорил: «Существует ли этот элемент в цепочке для ключей? Нет? Затем добавьте его. Упс! 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)];
}

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

person Olie    schedule 21.05.2015

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

Проблема у меня была интересная, мне нужно было сохранить только одно значение и я решил хранить его в поле kSecValueData. Это потому, что я видел другие сообщения об использовании цепочки для ключей и начал свою собственную реализацию, прежде чем обратиться к KeychainItemWrapper. Это вызвало следующую проблему: На первом устройстве, которое я тестировал (iPad 1-го поколения), я получал сообщение об ошибке в 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 (поиск кодов результатов)

person Kuoni    schedule 10.07.2013
comment
Как я могу найти элемент, который я сохранил ранее? Я перечислил все элементы таким образом: ios">stackoverflow.com/questions/10966969/, и его нет. Где он прячется? - person below; 02.10.2014

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

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

Разрешение на совместное использование связки ключей

person Seeler99    schedule 10.10.2016