Co sprawia, że ​​brelok jest wyjątkowy (w iOS)?

104

Moje pytanie dotyczy pęków kluczy w iOS (iPhone, iPad, ...). Myślę (ale nie jestem pewien), że implementacja pęków kluczy pod Mac OS X rodzi to samo pytanie z tą samą odpowiedzią.


iOS udostępnia pięć typów (klas) elementów pęku kluczy. Aby kSecClassokreślić typ, musisz wybrać jedną z tych pięciu wartości dla klucza :

kSecClassGenericPassword  used to store a generic password
kSecClassInternetPassword used to store an internet password
kSecClassCertificate      used to store a certificate
kSecClassKey              used to store a kryptographic key
kSecClassIdentity         used to store an identity (certificate + private key)

Po długim czasie czytania dokumentacji jabłka, blogi i wpisy w forum, dowiedziałem się, że pozycja brelok typu kSecClassGenericPassworddostaje swoją wyjątkowość z atrybutami kSecAttrAccessGroup, kSecAttrAccounta kSecAttrService.

Jeśli te trzy atrybuty w żądaniu 1 są takie same jak w żądaniu 2, otrzymasz ten sam ogólny element pęku kluczy haseł, niezależnie od innych atrybutów. Jeśli jeden (lub dwa lub wszystkie) z tych atrybutów zmieni swoją wartość, otrzymasz różne przedmioty.

Ale kSecAttrServicejest dostępny tylko dla elementów typu kSecClassGenericPassword, więc nie może być częścią „unikalnego klucza” przedmiotu innego typu i wydaje się, że nie ma dokumentacji, która jasno wskazywałaby, które atrybuty jednoznacznie określają przedmiot pęku kluczy.

Przykładowy kod w klasie „KeychainItemWrapper” elementu „GenericKeychain” używa tego atrybutu, kSecAttrGenericaby uczynić element unikalnym, ale jest to błąd. Tylko dwa wpisy w tym przykładzie są przechowywane jako dwa odrębne wpisy, ponieważ kSecAttrAccessGroupsą różne (jeden ma ustawioną grupę dostępu, a drugi umożliwia zwolnienie). Jeśli spróbujesz dodać drugie hasło bez grupy dostępu, używając Apple KeychainItemWrapper, nie powiedzie się.

Więc proszę, odpowiedz na moje pytania:

  • Czy to prawda, że połączenie kSecAttrAccessGroup, kSecAttrAccounti kSecAttrServicejest „unikalny klucz” z pęku kluczy elementu, którego kSecClass jest kSecClassGenericPassword?
  • Jakie atrybuty sprawiają, że przedmiot pęku kluczy jest wyjątkowy, jeśli kSecClassnie jest kSecClassGenericPassword?
Hubert Schölnast
źródło
1
Jest tutaj wpis na blogu na ten temat.
bobobobo

Odpowiedzi:

179

Klucze podstawowe są następujące (pochodzące z plików open source firmy Apple, zobacz Schema.m4 , KeySchema.m4 i SecItem.cpp ):

  • W przypadku elementu klasy pęku kluczy kSecClassGenericPasswordkluczem podstawowym jest kombinacja kSecAttrAccounti kSecAttrService.
  • Dla pęku kluczy pozycji klasy kSecClassInternetPassword, klucz podstawowy jest kombinacją kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPorti kSecAttrPath.
  • Dla pęku kluczy pozycji klasy kSecClassCertificate, klucz podstawowy jest kombinacją kSecAttrCertificateType, kSecAttrIssueri kSecAttrSerialNumber.
  • Dla pęku kluczy pozycji klasy kSecClassKey, klucz podstawowy jest kombinacją kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrEffectiveKeySize, i twórca, datę rozpoczęcia i zakończenia, które nie są narażone przez SecItem jeszcze.
  • W przypadku elementu klasy pęku kluczy kSecClassIdentitynie znalazłem informacji o polach klucza podstawowego w plikach typu open source, ale ponieważ tożsamość jest połączeniem klucza prywatnego i certyfikatu, zakładam, że klucz podstawowy to kombinacja klucza podstawowego pola dla kSecClassKeyi kSecClassCertificate.

Ponieważ każdy element pęku kluczy należy do grupy dostępu do pęku kluczy, wydaje się, że grupa dostępu do pęku kluczy (pole kSecAttrAccessGroup) jest polem dodanym do wszystkich tych kluczy podstawowych.

Tammo Freese
źródło
Brzmi jak naprawdę dobra odpowiedź! Dziękuję Ci! Sprawdzę to i chcę poczekać dzień lub dwa na dodatkowe komentarze od innych użytkowników, ale jesteś gorącym kandydatem na +50 punktów z bounty.
Hubert Schölnast
3
Świetna odpowiedź! Od kilku dni pracuję nad wdrożeniem ogólnego opakowania pęku kluczy dla certyfikatów i kluczy prywatnych. To znacznie różni się od przykładowego kodu Apple, który przechowuje tylko poświadczenia ciągów (nazwa użytkownika / hasło). Mam jednak okazało się, że po ustawieniu kSecClasssię kSecClassCertificatelub kSecClassKeykontrole Breloki również wtedy, gdy wejście (the value) jest już zapisany. Zapobiega to dwukrotnemu dodawaniu tego samego certyfikatu lub klucza. Również jeśli określisz inny kSecAttrApplicationTagklucz (który musi być unikalny w odniesieniu do powyższego postu), zakończy się niepowodzeniem.
Chris,
1
Pomocne może być myślenie o kSecClassatrybucie jako nazwie tabeli , a określone powyżej wartości tylko jako primary keyo odpowiedniej tabeli.
bobobobo
2
Jaka jest semantyka kSecAttrAccounti kSecAttrService? - czy może programista może wybrać jakąkolwiek semantykę, którą zdecyduje?
wcochran
1
kSecAttrServicesłuży do przechowywania usługi, kSecAttrAccountsłuży do przechowywania nazwy konta. Można w nich przechowywać różne rzeczy, ale może to być mylące.
Tammo Freese
9

Któregoś dnia napotkałem błąd (na iOS 7.1), który jest związany z tym pytaniem. Używałem SecItemCopyMatchingodczytać kSecClassGenericPasswordpozycję i powracał errSecItemNotFound(-25300), mimo kSecAttrAccessGroup, kSecAttrAccounti kSecAttrServicewszystkie były dopasowując pozycję w pęku kluczy.

W końcu doszedłem do wniosku, że kSecAttrAccessibleto nie pasuje. Wartość w pęku kluczy miała pdmn = dk ( kSecAttrAccessibleAlways), ale używałem kSecAttrAccessibleWhenUnlocked.

Oczywiście ta wartość nie jest potrzebna w pierwszej kolejności SecItemCopyMatching, ale OSStatusnie była ona errSecParamani errSecBadReqtylko errSecItemNotFound(-25300), co utrudniało jej znalezienie.

Ponieważ SecItemUpdatedoświadczyłem tego samego problemu, ale w tej metodzie nawet użycie tego samego kSecAttrAccessiblew queryparametrze nie zadziałało. Dopiero całkowite usunięcie tego atrybutu naprawiło to.

Mam nadzieję, że ten komentarz pozwoli niektórym z was zaoszczędzić kilka cennych chwil związanych z debugowaniem.

izik lisbon
źródło
4

Odpowiedź udzielona przez @Tammo Freese wydaje się być poprawna (ale nie wymieniając wszystkich kluczy głównych). Szukałem jakiegoś dowodu w dokumentacji. Wreszcie znaleziono:

Dokumentacja Apple wymieniająca klucze główne dla każdej klasy sekretów (cytat poniżej):

System traktuje element jako duplikat dla danego pęku kluczy, gdy ten pęku kluczy ma już element tej samej klasy z tym samym zestawem złożonych kluczy głównych. Każda klasa elementu pęku kluczy ma inny zestaw kluczy podstawowych, chociaż kilka atrybutów jest wspólnych dla wszystkich klas. W szczególności, w stosownych przypadkach, kSecAttrSynchronizable i kSecAttrAccessGroup są częścią zestawu kluczy podstawowych . Dodatkowe klucze podstawowe dla poszczególnych klas są wymienione poniżej:

  • W przypadku haseł ogólnych do kluczy podstawowych należą kSecAttrAccount i kSecAttrService.
  • W przypadku haseł internetowych do kluczy głównych należą kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort i kSecAttrPath.
  • W przypadku certyfikatów klucze podstawowe obejmują kSecAttrCertificateType, kSecAttrIssuer i kSecAttrSerialNumber.
  • W przypadku elementów kluczowych klucze podstawowe obejmują kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits i kSecAttrEffectiveKeySize.
  • W przypadku elementów tożsamości, które są połączonymi razem certyfikatem i kluczem prywatnym, klucze podstawowe są takie same, jak w przypadku certyfikatu. Ponieważ klucz prywatny może być poświadczony więcej niż jeden raz, o tożsamości decyduje niepowtarzalność certyfikatu.
Julian Król
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
pwc,
zgodził się, chociaż w tym przypadku oznaczało to skopiowanie całego linku.
Julian Król
0

Oto kolejna przydatna informacja na temat wyjątkowości elementu pęku kluczy, którą można znaleźć w sekcji „Zapewnianie możliwości wyszukiwania” na tej stronie z dokumentami firmy Apple .

Aby móc później znaleźć przedmiot, wykorzystasz swoją wiedzę o jego atrybutach. W tym przykładzie serwer i konto są cechami wyróżniającymi element. W przypadku atrybutów stałych (w tym przypadku serwera) użyj tej samej wartości podczas wyszukiwania. W przeciwieństwie do tego atrybut konta jest dynamiczny, ponieważ zawiera wartość podaną przez użytkownika w czasie wykonywania. O ile Twoja aplikacja nigdy nie dodaje podobnych elementów o różnych atrybutach (takich jak hasła do różnych kont na tym samym serwerze), możesz pominąć te atrybuty dynamiczne jako parametry wyszukiwania i zamiast tego pobrać je wraz z elementem. W rezultacie podczas wyszukiwania hasła otrzymujesz również odpowiednią nazwę użytkownika.

Jeśli Twoja aplikacja dodaje elementy o różnych atrybutach dynamicznych, będziesz potrzebować sposobu wyboru między nimi podczas pobierania. Jedną z opcji jest zapisanie informacji o przedmiotach w inny sposób. Na przykład, jeśli przechowujesz rekordy użytkowników w modelu Core Data, przechowujesz tam nazwę użytkownika po użyciu usług pęku kluczy do przechowywania pola hasła. Później użyjesz nazwy użytkownika pobranej z modelu danych, aby warunkować wyszukiwanie hasła.

W innych przypadkach sensowne może być dalsze scharakteryzowanie przedmiotu poprzez dodanie większej liczby atrybutów. Na przykład możesz uwzględnić kSecAttrLabelatrybut w pierwotnym zapytaniu dodawania, podając ciąg znaków oznaczający element w określonym celu. Następnie będziesz mógł użyć tego atrybutu, aby później zawęzić wyszukiwanie.

kSecClassInternetPasswordW przykładzie użyto elementu klasy , ale jest tam uwaga, która mówi:

Usługi pęku kluczy oferują również pokrewną klasę pozycji kSecClassGenericPassword. Hasła ogólne są pod wieloma względami podobne do haseł internetowych, ale brakuje im pewnych atrybutów specyficznych dla dostępu zdalnego (na przykład nie mają atrybutu kSecAttrServer). Jeśli nie potrzebujesz tych dodatkowych atrybutów, użyj zamiast tego ogólnego hasła.

Aleksandar
źródło