В настоящее время я храню имя пользователя (адрес электронной почты) и соленый хеш адреса электронной почты и пароля в iOS KeyChain. Я использую версию ARC, найденную здесь .
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
[wrapper setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
[wrapper setObject:email forKey:(__bridge id)kSecAttrAccount];
[wrapper setObject:token forKey:(__bridge id)kSecValueData];
Все это отлично работает, когда мне нужно вытащить токен для моих сетевых вызовов, пока приложение активно. Он работает для входа в систему из чистого запуска, а также для всех сетевых вызовов во всем. Проблема начинается, когда приложение работает в фоновом режиме.
Имейте в виду, что это происходит спорадически, и мне еще предстоит привязать это к конкретной версии iOS или устройству.
Пользователь посещает место (мониторинг региона), и я хочу обновить сервер с его статусом. Я пытаюсь вытащить токен из связки ключей так же, как и для любого другого сетевого вызова, и обновить статус. Но для некоторых пользователей значение равно нулю. Без него я не могу обновить сетевую информацию. Почему это сработает для большинства, но не для небольшого процента?
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];
Я вернулся к версии keychainwrapper без ARC, но все равно получаю те же результаты. Буду признателен за любые отзывы по этому поводу. Это лишь небольшая часть моих пользователей, но это проблема, которую я хотел бы исправить и о которой не беспокоиться. Заранее спасибо.
Кроме того, вся моя фоновая работа настроена в backgroundTask, чтобы предотвратить истечение времени ожидания. У меня нет проблем с работой, связанной с связкой ключей, но я не позволяю этому идти вперед, пока мой токен не будет заполнен.
ИЗМЕНИТЬ Я выяснил свою проблему с цепочкой ключей, не извлекающей значения из фона. Я отправлю ответ ниже и приму его, так как считаю, что этот вопрос может стать ценным для других позже.
источник
…AccessibleAlways
если это вообще возможно, или сохраните токен, который предоставляет только ограниченные привилегии (например, токен, который позволяет вам читать новые элементы ленты, но не публиковать). Тем самым вы явно отказываетесь от определенного уровня шифрования. Если ваше приложение может дождаться первой разблокировки, возможно, лучше всего будет использовать…AfterFirstUnlock
и направить ваших пользователей сначала разблокировать свои устройства.Используйте
kSecAttrAccessibleAfterFirstUnlock
вместоkSecAttrAccessibleAlways
.Из документации Apple :
источник
kSecAttrAccessibleAlways
он уже устарелВ моем случае watchOS2 получает доступ к данным связки ключей на стороне iOS.
Вначале используется kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Я могу читать данные независимо от того, заблокирован iPhone или нет. Меня очень сбивает с толку то, что я получаю сообщение об ошибке, когда часы пытаются получить доступ к связке ключей:: SecTrustEvaluate [лист IssuerCommonName SubjectCommonName]
В некоторых случаях это будет:: SecOSStatusWith error: [- 25308] Error Domain = NSOSStatusErrorDomain Code = -25308 «ks_crypt: e00002e2 не удалось выполнить элемент« oe »(класс 6, сумка: 0) Попытка доступа к элементу, пока связка ключей заблокирована. " UserInfo = {NSDescription = ks_crypt: e00002e2 не удалось выполнить элемент «oe» (класс 6, сумка: 0) Попытка доступа к элементу при заблокированной связке ключей.}
Я обновлю свой ответ, если получу больше информации.
источник
Это могло произойти из-за политики защиты данных Apple, которая в некоторой степени неясна с точки зрения разработчиков. Обходной путь - когда запущенное приложение проверяет, доступна ли связка ключей или нет, если она недоступна, вы можете убить свое приложение (с правильным всплывающим окном) в зависимости от типа вашего приложения.
+(BOOL) isKeychainAccessible { NSString *keychainTestKey = @"keychainTestKey"; NSString *keychainTestValue = @"keychainTestValue"; [self createKeychainValue:keychainTestValue forIdentifier:keychainTestKey]; NSString *loadedValue = [self keychainStringFromMatchingIdentifier:keychainTestKey]; [self deleteItemFromKeychainWithIdentifier:keychainTestKey]; return ([keychainTestValue isEqualToString: loadedValue]); }
источник