¿Qué hace que un elemento de llavero sea único (en iOS)?

104

Mi pregunta se refiere a los llaveros en iOS (iPhone, iPad, ...). Creo (pero no estoy seguro) que la implementación de llaveros en Mac OS X plantea la misma pregunta con la misma respuesta.


iOS proporciona cinco tipos (clases) de elementos de llavero. Debe elegir uno de esos cinco valores para que la clave kSecClassdetermine el tipo:

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)

Después de mucho tiempo de leer la documentación de las manzanas, los blogs y las entradas al foro, descubrí que un elemento de tipo llavero kSecClassGenericPasswordobtiene su singularidad de los atributos kSecAttrAccessGroup,kSecAttrAccount y kSecAttrService.

Si esos tres atributos en la solicitud 1 son los mismos que en la solicitud 2, entonces recibirá el mismo elemento de llavero de contraseña genérico, independientemente de cualquier otro atributo. Si uno (o dos o todos) de estos atributos cambia su valor, obtendrá elementos diferentes.

Pero kSecAttrServicesolo está disponible para artículos de tipokSecClassGenericPassword , por lo que no puede ser parte de la "clave única" de un elemento de ningún otro tipo, y parece que no hay documentación que indique claramente qué atributos determinan de manera única un elemento de llavero.

El código de muestra en la clase "KeychainItemWrapper" de "GenericKeychain" usa el atributo kSecAttrGenericpara hacer que un elemento sea único, pero esto es un error. Las dos entradas en este ejemplo solo se almacenan como dos entradas distintas, porque kSecAttrAccessGroupson diferentes (una tiene el grupo de acceso configurado, la otra lo deja libre). Si intenta agregar una segunda contraseña sin un grupo de acceso, utilizando Apple KeychainItemWrapper, fallará.

Entonces, por favor, responda mis preguntas:

  • Es cierto, que la combinación de kSecAttrAccessGroup, kSecAttrAccounty kSecAttrServicees la "clave única" de un elemento llavero cuya kSecClass es kSecClassGenericPassword?
  • ¿Qué atributos hacen que un elemento de llavero sea único si kSecClassno lo es kSecClassGenericPassword?
Hubert Schölnast
fuente
1
Hay una entrada de blog aquí sobre esto.
bobobobo

Respuestas:

179

Las claves principales son las siguientes (derivadas de archivos de código abierto de Apple, consulte Schema.m4 , KeySchema.m4 y SecItem.cpp ):

  • Para un elemento de clase de llavero kSecClassGenericPassword, la clave principal es la combinación de kSecAttrAccounty kSecAttrService.
  • Para un elemento de llavero de la clase kSecClassInternetPassword, la clave principal es la combinación de kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPorty kSecAttrPath.
  • Para un elemento de llavero de la clase kSecClassCertificate, la clave principal es la combinación de kSecAttrCertificateType, kSecAttrIssuery kSecAttrSerialNumber.
  • Para un elemento de llavero de la clase kSecClassKey, la clave principal es la combinación de kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrEffectiveKeySize, y el creador, fecha de inicio y de finalización que no están expuestos por SecItem todavía.
  • Para un elemento de llavero de clase, kSecClassIdentityno he encontrado información sobre los campos de clave principal en los archivos de código abierto, pero como una identidad es la combinación de una clave privada y un certificado, asumo que la clave principal es la combinación de la clave principal. campos para kSecClassKeyy kSecClassCertificate.

Como cada elemento de llavero pertenece a un grupo de acceso de llavero, parece que el grupo de acceso de llavero (campo kSecAttrAccessGroup) es un campo agregado a todas estas claves primarias.

Tammo Freese
fuente
¡Suena como una muy buena respuesta! ¡Gracias! Lo comprobaré y quiero esperar uno o dos días para recibir comentarios adicionales de otros usuarios, pero eres un candidato atractivo para los +50 puntos de la recompensa.
Hubert Schölnast
3
¡Gran respuesta! Estoy trabajando durante algunos días en la implementación de un contenedor de llaveros genérico para certificados y claves privadas. Eso es muy diferente al código de muestra de Apple que solo almacena credenciales de cadena (nombre de usuario / contraseña). Sin embargo, descubrí que cuando configura el kSecClassto kSecClassCertificateo kSecClassKeyel llavero también verifica si la entrada (the value) ya está almacenada. Esto evita agregar el mismo certificado o clave dos veces. Además, si especifica una kSecAttrApplicationTagclave diferente (que debe ser única, con respecto a la publicación anterior), fallará.
Chris
1
Puede ser útil pensar en el kSecClassatributo como el nombre de la tabla y los valores especificados arriba como solo el primary keyde la tabla respectiva.
bobobobo
2
¿Cuál es la semántica de kSecAttrAccounty kSecAttrService? - ¿O puede el programador elegir cualquier semántica que decida?
wcochran
1
kSecAttrServicees para almacenar el servicio, kSecAttrAccountes para almacenar el nombre de la cuenta. Puede almacenar diferentes cosas en ellos, pero eso puede resultar confuso.
Tammo Freese
9

El otro día encontré un error (en iOS 7.1) relacionado con esta pregunta. Estaba usando SecItemCopyMatchingpara leer un kSecClassGenericPasswordartículo y seguía volviendoerrSecItemNotFound (-25300) a pesar de que kSecAttrAccessGroup, kSecAttrAccounty kSecAttrServicetodos coincidían con el artículo en el llavero.

Eventualmente me di cuenta de que kSecAttrAccessible no coincidía. El valor en el llavero tenía pdmn = dk ( kSecAttrAccessibleAlways), pero estaba usando kSecAttrAccessibleWhenUnlocked.

Por supuesto, este valor no es necesario en primer lugar para SecItemCopyMatching, pero elOSStatus no era errSecParamni, errSecBadReqsino solo errSecItemNotFound(-25300), lo que hizo que fuera un poco difícil de encontrar.

por SecItemUpdate he experimentado el mismo problema, pero en este método, incluso usar el mismo kSecAttrAccessibleen el queryparámetro no funcionó. Solo la eliminación completa de este atributo lo solucionó.

Espero que este comentario les ahorre unos preciosos momentos de depuración para algunos de ustedes.

izik lisboa
fuente
4

La respuesta dada por @Tammo Freese parece ser correcta (pero sin mencionar todas las claves primarias). Estaba buscando alguna prueba en la documentación. Finalmente encontrado:

Documentación de Apple que menciona las claves primarias para cada clase de secreto (cita a continuación):

El sistema considera que un elemento es un duplicado de un llavero dado cuando ese llavero ya tiene un elemento de la misma clase con el mismo conjunto de claves primarias compuestas. Cada clase de elemento de llavero tiene un conjunto diferente de claves primarias, aunque algunos atributos se usan en común en todas las clases. En particular, cuando corresponda, kSecAttrSynchronizable y kSecAttrAccessGroup son parte del conjunto de claves primarias . Las claves primarias adicionales por clase se enumeran a continuación:

  • Para contraseñas genéricas, las claves primarias incluyen kSecAttrAccount y kSecAttrService.
  • Para las contraseñas de Internet, las claves principales incluyen kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort y kSecAttrPath.
  • Para certificados, las claves primarias incluyen kSecAttrCertificateType, kSecAttrIssuer y kSecAttrSerialNumber.
  • Para los elementos clave, las claves primarias incluyen kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits y kSecAttrEffectiveKeySize.
  • Para los elementos de identidad, que son un certificado y una clave privada agrupados, las claves primarias son las mismas que para un certificado. Dado que una clave privada puede certificarse más de una vez, la singularidad del certificado determina la de la identidad.
Julian Król
fuente
Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada. - De la crítica
pwc
estuvo de acuerdo, aunque en este caso significó copiar todo el enlace.
Julian Król
0

Aquí hay otra pieza de información útil sobre la singularidad de un elemento de llavero, que se encuentra en la sección "Garantizar la capacidad de búsqueda" de esta página de documentos de Apple .

Para poder encontrar el artículo más tarde, utilizará su conocimiento de sus atributos. En este ejemplo, el servidor y la cuenta son las características distintivas del artículo. Para atributos constantes (aquí, el servidor), use el mismo valor durante la búsqueda. Por el contrario, el atributo de cuenta es dinámico porque contiene un valor proporcionado por el usuario en tiempo de ejecución.Siempre que su aplicación nunca agregue elementos similares con diferentes atributos (como contraseñas para diferentes cuentas en el mismo servidor), puede omitir estos atributos dinámicos como parámetros de búsqueda y, en su lugar, recuperarlos junto con el elemento. Como resultado, cuando busca la contraseña, también obtiene el nombre de usuario correspondiente.

Si su aplicación agrega elementos con diferentes atributos dinámicos, necesitará una forma de elegir entre ellos durante la recuperación. Una opción es registrar información sobre los elementos de otra forma. Por ejemplo, si mantiene registros de usuarios en un modelo de datos básicos, almacena el nombre de usuario allí después de usar los servicios de llavero para almacenar el campo de contraseña. Posteriormente, utiliza el nombre de usuario extraído de su modelo de datos para condicionar la búsqueda de la contraseña.

En otros casos, puede tener sentido caracterizar aún más el artículo agregando más atributos. Por ejemplo, puede incluir el kSecAttrLabelatributo en la consulta de adición original, proporcionando una cadena que marca el elemento para el propósito particular. Entonces podrá utilizar este atributo para acotar su búsqueda más adelante.

El elemento de clase kSecClassInternetPasswordse usó en el ejemplo, pero hay una nota que dice:

Los servicios de llaveros también ofrecen la clase de elemento kSecClassGenericPassword relacionada. Las contraseñas genéricas son similares en la mayoría de los aspectos a las contraseñas de Internet, pero carecen de ciertos atributos específicos para el acceso remoto (por ejemplo, no tienen un atributo kSecAttrServer). Cuando no necesite estos atributos adicionales, utilice una contraseña genérica en su lugar.

Aleksandar
fuente