Qu'est-ce qui rend un élément de trousseau unique (sous iOS)?

104

Ma question concerne les porte-clés sous iOS (iPhone, iPad, ...). Je pense (mais je ne suis pas sûr) que l'implémentation de keychains sous Mac OS X soulève la même question avec la même réponse.


iOS fournit cinq types (classes) d'éléments de trousseau. Vous devez choisir l'une de ces cinq valeurs pour la clé kSecClassafin de déterminer le type:

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)

Après une longue période de documentation des pommes de lecture, blogs et forum-entrées, j'ai découvert qu'un élément clé de type kSecClassGenericPasswordobtient son unicité des attributs kSecAttrAccessGroup, kSecAttrAccountet kSecAttrService.

Si ces trois attributs dans la demande 1 sont les mêmes que dans la demande 2, vous recevez le même élément de trousseau de mot de passe générique, quels que soient les autres attributs. Si un (ou deux ou tous) de ces attributs modifie sa valeur, vous obtenez des éléments différents.

Mais kSecAttrServicen'est disponible que pour les éléments de type kSecClassGenericPassword, donc il ne peut pas faire partie de la "clé unique" d'un élément d'un autre type, et il ne semble y avoir aucune documentation indiquant clairement quels attributs déterminent de manière unique un élément de trousseau.

L'exemple de code dans la classe «KeychainItemWrapper» de «GenericKeychain» utilise l'attribut kSecAttrGenericpour rendre un élément unique, mais il s'agit d'un bogue. Les deux entrées de cet exemple uniquement sont stockées sous la forme de deux entrées distinctes, car elles kSecAttrAccessGroupsont différentes (l'une a le groupe d'accès défini, l'autre le laisse libre). Si vous essayez d'ajouter un deuxième mot de passe sans groupe d'accès, en utilisant Apple KeychainItemWrapper, vous échouerez.

Alors, s'il vous plaît, répondez à mes questions:

  • Est-il vrai que la combinaison de kSecAttrAccessGroup, kSecAttrAccountet kSecAttrServiceest la "clé unique" d'un élément de trousseau dont kSecClass est kSecClassGenericPassword?
  • Quels attributs rendent un élément de trousseau unique si ce kSecClassn'est pas le cas kSecClassGenericPassword?
Hubert Schölnast
la source
1
Il y a une entrée de blog ici à ce sujet.
bobobobo

Réponses:

179

Les clés primaires sont les suivantes (dérivées de fichiers open source d'Apple, voir Schema.m4 , KeySchema.m4 et SecItem.cpp ):

  • Pour un élément de classe de trousseau kSecClassGenericPassword, la clé primaire est la combinaison de kSecAttrAccountet kSecAttrService.
  • Pour un élément clé de la classe kSecClassInternetPassword, la clé primaire est la combinaison de kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPortet kSecAttrPath.
  • Pour un élément de classe de trousseau kSecClassCertificate, la clé primaire est la combinaison de kSecAttrCertificateType, kSecAttrIssueret kSecAttrSerialNumber.
  • Pour un élément clé de la classe kSecClassKey, la clé primaire est la combinaison de kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrEffectiveKeySize, et le créateur, la date de début et de fin qui ne sont pas exposés par SecItem encore.
  • Pour un élément de trousseau de classe, kSecClassIdentityje n'ai pas trouvé d'informations sur les champs de clé primaire dans les fichiers open source, mais comme une identité est la combinaison d'une clé privée et d'un certificat, je suppose que la clé primaire est la combinaison de la clé primaire champs pour kSecClassKeyet kSecClassCertificate.

Comme chaque élément du trousseau appartient à un groupe d'accès au trousseau, il semble que le groupe d'accès au trousseau (champ kSecAttrAccessGroup) est un champ ajouté à toutes ces clés primaires.

Tammo Freese
la source
Sonne comme une très bonne réponse! Je vous remercie! Je vais le vérifier et je veux attendre un ou deux jours pour les commentaires supplémentaires des autres utilisateurs, mais vous êtes un candidat chaud pour les +50 points de la prime.
Hubert Schöln le
3
Très bonne réponse! Je travaille depuis quelques jours sur l'implémentation d'un wrapper de trousseau générique pour les certificats et les clés privées. C'est très différent de l'exemple de code d'Apple qui ne stocke que les informations d'identification de chaîne (nom d'utilisateur / mot de passe). Cependant, je l' ai découvert que lorsque vous définissez l' kSecClassà kSecClassCertificateou kSecClassKeyles contrôles porte - clés même si l'entrée (le value) est déjà enregistré. Cela empêche d'ajouter deux fois le même certificat ou la même clé. De plus, si vous spécifiez une kSecAttrApplicationTagclé différente pour une clé (qui doit être unique, concernant le message ci-dessus), cela échouera.
Chris
1
Il peut être utile de considérer l' kSecClassattribut comme le nom de la table et les valeurs spécifiées ci-dessus comme juste le nom primary keyde la table respective.
bobobobo du
2
Quelle est la sémantique de kSecAttrAccountet kSecAttrService? - ou le programmeur peut-il choisir une sémantique qu'elle décide?
wcochran
1
kSecAttrServiceest pour stocker le service, kSecAttrAccountest pour stocker le nom du compte. Vous pouvez y stocker différentes choses, mais cela peut prêter à confusion.
Tammo Freese
9

J'ai rencontré un bug l'autre jour (sur iOS 7.1) lié à cette question. J'utilisais SecItemCopyMatchingpour lire un kSecClassGenericPasswordarticle et il a continué à revenir errSecItemNotFound(-25300) même si kSecAttrAccessGroup, kSecAttrAccountet kSecAttrServicetous correspondaient à l'article dans le trousseau.

Finalement, j'ai compris que kSecAttrAccessiblecela ne correspondait pas. La valeur dans le trousseau contenait pdmn = dk ( kSecAttrAccessibleAlways), mais j'utilisais kSecAttrAccessibleWhenUnlocked.

Bien sûr, cette valeur n'est pas nécessaire en premier lieu pour SecItemCopyMatching, mais ce OSStatusn'était pas errSecParamni errSecBadReqmais juste errSecItemNotFound(-25300), ce qui l'a rendu un peu difficile à trouver.

Car SecItemUpdatej'ai rencontré le même problème, mais dans cette méthode, même utiliser la même chose kSecAttrAccessibledans le queryparamètre ne fonctionnait pas. Seule la suppression complète de cet attribut l'a corrigé.

J'espère que ce commentaire sauvera quelques précieux moments de débogage pour certains d'entre vous.

izik lisbonne
la source
4

La réponse donnée par @Tammo Freese semble correcte (mais sans mentionner toutes les clés primaires). Je cherchais des preuves dans la documentation. Finalement trouvé:

Documentation Apple mentionnant les clés primaires pour chaque classe de secret (citation ci-dessous):

Le système considère qu'un élément est un doublon pour un trousseau donné lorsque ce trousseau a déjà un article de la même classe avec le même ensemble de clés primaires composites. Chaque classe d'élément de trousseau a un ensemble différent de clés primaires, bien que quelques attributs soient utilisés en commun dans toutes les classes. En particulier, le cas échéant, kSecAttrSynchronizable et kSecAttrAccessGroup font partie de l'ensemble des clés primaires . Les clés primaires supplémentaires par classe sont répertoriées ci-dessous:

  • Pour les mots de passe génériques, les clés primaires incluent kSecAttrAccount et kSecAttrService.
  • Pour les mots de passe Internet, les clés primaires incluent kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort et kSecAttrPath.
  • Pour les certificats, les clés primaires incluent kSecAttrCertificateType, kSecAttrIssuer et kSecAttrSerialNumber.
  • Pour les éléments clés, les clés primaires incluent kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits et kSecAttrEffectiveKeySize.
  • Pour les éléments d'identité, qui sont un certificat et une clé privée regroupés, les clés primaires sont les mêmes que pour un certificat. Étant donné qu'une clé privée peut être certifiée plusieurs fois, l'unicité du certificat détermine celle de l'identité.
Julian Król
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change. - De l'avis
pwc
d'accord, bien que dans ce cas, cela signifiait copier l'ensemble du lien.
Julian Król
0

Voici une autre information utile sur l'unicité d'un élément de trousseau, disponible dans la section «Assurer la recherche» de cette page de documentation Apple .

Pour pouvoir retrouver l'élément plus tard, vous allez utiliser votre connaissance de ses attributs. Dans cet exemple, le serveur et le compte sont les caractéristiques distinctives de l'élément. Pour les attributs constants (ici, le serveur), utilisez la même valeur lors de la recherche. En revanche, l'attribut de compte est dynamique, car il contient une valeur fournie par l'utilisateur lors de l'exécution. Tant que votre application n'ajoute jamais d'éléments similaires avec des attributs variables (tels que des mots de passe pour différents comptes sur le même serveur), vous pouvez omettre ces attributs dynamiques en tant que paramètres de recherche et les récupérer à la place avec l'élément. En conséquence, lorsque vous recherchez le mot de passe, vous obtenez également le nom d'utilisateur correspondant.

Si votre application ajoute des éléments avec différents attributs dynamiques, vous aurez besoin d'un moyen de choisir parmi eux lors de la récupération. Une option consiste à enregistrer les informations sur les éléments d'une autre manière. Par exemple, si vous conservez des enregistrements d'utilisateurs dans un modèle Core Data, vous y stockez le nom d'utilisateur après avoir utilisé les services de trousseau pour stocker le champ de mot de passe. Plus tard, vous utiliserez le nom d'utilisateur extrait de votre modèle de données pour conditionner la recherche du mot de passe.

Dans d'autres cas, il peut être judicieux de caractériser davantage l'élément en ajoutant plus d'attributs. Par exemple, vous pouvez inclure l' kSecAttrLabelattribut dans la requête d'ajout d'origine, en fournissant une chaîne qui marque l'élément dans un but particulier. Vous pourrez ensuite utiliser cet attribut pour affiner votre recherche ultérieurement.

L'élément de classe a kSecClassInternetPasswordété utilisé dans l'exemple, mais il y a une note qui dit:

Les services de trousseau offrent également la classe d'élément kSecClassGenericPassword associée. Les mots de passe génériques sont similaires à bien des égards aux mots de passe Internet, mais ils manquent de certains attributs spécifiques à l'accès à distance (par exemple, ils n'ont pas d'attribut kSecAttrServer). Lorsque vous n'avez pas besoin de ces attributs supplémentaires, utilisez plutôt un mot de passe générique.

Aleksandar
la source