Obecnie przechowuję nazwę użytkownika (adres e-mail) i zasolony skrót adresu e-mail i hasła w łańcuchu kluczy iOS. Używam wersji zweryfikowanej przez ARC, znalezionej tutaj .
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];
To wszystko działa dobrze, gdy muszę wyciągnąć token dla moich połączeń sieciowych, gdy aplikacja jest aktywna. Działa przy logowaniu się z czystego startu, a także przy wszystkich połączeniach sieciowych. Problem zaczyna się, gdy aplikacja działa w tle.
Pamiętaj, że zdarza się to tylko sporadycznie i jeszcze nie przypisałem tego do konkretnej wersji lub urządzenia iOS.
Użytkownik podróżuje do lokalizacji (monitorowanie regionu) i chcę zaktualizować serwer o ich status. Próbuję wyciągnąć token z pęku kluczy, tak samo jak przy każdym innym połączeniu sieciowym, i aktualizuję status. Ale dla niektórych użytkowników wartość wynosi zero. Bez tego nie mogę aktualizować sieci. Dlaczego miałoby to działać dla większości, ale nie dla małego odsetka?
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];
Wróciłem do wersji keychainwrapper innej niż ARC, ale nadal otrzymuję te same wyniki. Byłbym wdzięczny za wszelkie uwagi na ten temat. To tylko niewielka część moich użytkowników, ale jest to problem, który chciałbym naprawić i którym się nie martwię. Z góry dziękuję.
Ponadto cała moja praca w tle jest skonfigurowana w tle, aby zapobiec przekroczeniu limitu czasu. Nie mam żadnych problemów z pracą związaną z pękiem kluczy, ale nie pozwalam na to, dopóki mój token nie zostanie zapełniony.
EDYCJA Zrozumiałem mój problem z tym, że pęku kluczy nie pobierają wartości z tła. Poniżej zamieszczę odpowiedź i zaakceptuję ją, ponieważ uważam, że to pytanie może później stać się wartościowe dla innych.
źródło
…AccessibleAlways
jeśli to w ogóle możliwe, lub przechowuj token, który zapewnia tylko ograniczone uprawnienia (np. Token, który umożliwia odczytywanie nowych elementów kanału, ale nie wysyłanie). W ten sposób wyraźnie rezygnujesz z poziomu szyfrowania. Jeśli Twoja aplikacja może zaczekać do pierwszego odblokowania, być może najlepiej byłoby użyć…AfterFirstUnlock
i skierować użytkowników, aby najpierw odblokowali swoje urządzenia.Posługiwać się
kSecAttrAccessibleAfterFirstUnlock
zamiastkSecAttrAccessibleAlways
.Z dokumentacji Apple :
źródło
kSecAttrAccessibleAlways
jest już przestarzałaW moim przypadku watchOS2 uzyskuje dostęp do danych pęku kluczy po stronie iOS.
Na początku używany jest kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Mogę odczytać dane niezależnie od tego, czy iPhone jest zablokowany, czy nie. Jest dla mnie bardzo mylące, że otrzymuję błąd, gdy zegarek próbuje uzyskać dostęp do pęku kluczy:: SecTrustEvaluate [liść IssuerCommonName SubjectCommonName]
W pewnym przypadku stanie się to:: SecOSStatusWith error: [- 25308] Error Domain = NSOSStatusErrorDomain Code = -25308 "ks_crypt: e00002e2 nie powiodło się" oe "item (class 6, bag: 0) Próbowano uzyskać dostęp do elementu, gdy pęku kluczy jest zablokowany. " UserInfo = {NSDescription = ks_crypt: e00002e2 nie udało się uzyskać elementu „oe” (klasa 6, torba: 0) Podjęto próbę uzyskania dostępu do elementu, gdy pęku kluczy jest zablokowany.}
Zaktualizuję odpowiedź, jeśli otrzymam więcej informacji.
źródło
Może się tak zdarzyć z powodu polityki ochrony danych firmy Apple, która jest na pewnym poziomie niejasna z punktu widzenia programistów. Obejście polega na tym, że po uruchomieniu aplikacji sprawdź, czy pęk kluczy jest dostępny, czy nie, jeśli nie jest dostępny, możesz zabić aplikację (z odpowiednim wyskakującym okienkiem) w zależności od typu aplikacji.
+(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]); }
źródło