O que torna um item de chaveiro único (no iOS)?

104

Minha pergunta diz respeito a chaveiros no iOS (iPhone, iPad, ...). Acho (mas não tenho certeza) que a implementação de chaveiros no Mac OS X levanta a mesma questão com a mesma resposta.


O iOS oferece cinco tipos (classes) de itens de chaveiro. Você deve escolher um desses cinco valores para a chave kSecClasspara determinar o 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)

Depois de muito tempo de documentação maçãs leitura, blogs e fórum-entradas, eu descobri que um item de chaveiro do tipo kSecClassGenericPasswordobtém sua singularidade dos atributos kSecAttrAccessGroup, kSecAttrAccounte kSecAttrService.

Se esses três atributos na solicitação 1 forem iguais aos da solicitação 2, você receberá o mesmo item de chaveiro de senha genérica, independentemente de quaisquer outros atributos. Se um (ou dois ou todos) desses atributos alterar seu valor, você obterá itens diferentes.

Mas kSecAttrServicesó está disponível para itens do tipo kSecClassGenericPassword, portanto, não pode fazer parte da "chave única" de um item de qualquer outro tipo e parece não haver documentação que indique claramente quais atributos determinam exclusivamente um item de chaveiro.

O código de amostra na classe "KeychainItemWrapper" de "GenericKeychain" usa o atributo kSecAttrGenericpara tornar um item único, mas isso é um bug. As duas entradas neste exemplo são armazenadas apenas como duas entradas distintas, porque kSecAttrAccessGroupsão diferentes (uma tem o grupo de acesso definido, a outra o deixa livre). Se você tentar adicionar uma 2ª senha sem um grupo de acesso, usando o da Apple KeychainItemWrapper, você falhará.

Então, por favor, responda às minhas perguntas:

  • É verdade, que a combinação de kSecAttrAccessGroup, kSecAttrAccounte kSecAttrServiceé a "chave única" de um item de chaveiro cuja kSecClass é kSecClassGenericPassword?
  • Quais atributos tornam um item de chaveiro único se kSecClassnão for kSecClassGenericPassword?
Hubert Schölnast
fonte
1
Há uma entrada de blog aqui sobre isso.
bobobobo

Respostas:

179

As chaves primárias são as seguintes (derivadas de arquivos de código aberto da Apple, consulte Schema.m4 , KeySchema.m4 e SecItem.cpp ):

  • Para um item de classe de chaveiro kSecClassGenericPassword, a chave primária é a combinação de kSecAttrAccounte kSecAttrService.
  • Para um item de chaveiro de classe kSecClassInternetPassword, a chave primária é a combinação de kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPorte kSecAttrPath.
  • Para um item de classe de chaveiro kSecClassCertificate, a chave primária é a combinação de kSecAttrCertificateType, kSecAttrIssuere kSecAttrSerialNumber.
  • Para um item de chaveiro de classe kSecClassKey, a chave primária é a combinação de kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrEffectiveKeySize, e do criador, data de início e fim data que não são expostos por SecItem ainda.
  • Para um item de classe de chaveiro kSecClassIdentity, não encontrei informações sobre os campos de chave primária nos arquivos de código aberto, mas como uma identidade é a combinação de uma chave privada e um certificado, presumo que a chave primária é a combinação da chave primária campos para kSecClassKeye kSecClassCertificate.

Como cada item de chaveiro pertence a um grupo de acesso de chaveiro, parece que o grupo de acesso de chaveiro (campo kSecAttrAccessGroup) é um campo adicionado a todas essas chaves primárias.

Tammo Freese
fonte
Parece uma resposta muito boa! Obrigado! Vou verificar e quero esperar um ou dois dias por comentários adicionais de outros usuários, mas você é um grande candidato aos +50 pontos da recompensa.
Hubert Schölnast
3
Ótima resposta! Estou trabalhando há alguns dias na implementação de um wrapper de Keychain genérico para certificados e chaves privadas. Isso é muito diferente do código de amostra da Apple que armazena apenas credenciais de string (nome de usuário / senha). No entanto, descobri que quando você define o kSecClasscomo kSecClassCertificateou kSecClassKeyo Keychain verifica também se a entrada (o value) já está armazenada. Isso evita adicionar o mesmo certificado ou chave duas vezes. Além disso, se você especificar uma kSecAttrApplicationTagchave diferente (que deve ser única, em relação ao post acima), ela falhará.
Chris
1
Pode ser útil pensar no kSecClassatributo como o nome da tabela e os valores especificados acima como apenas os primary keyda respectiva tabela.
bobobobo
2
Qual é a semântica de kSecAttrAccounte kSecAttrService? - ou o programador pode escolher qualquer semântica que ele decidir?
wcochran
1
kSecAttrServiceé para armazenar o serviço, kSecAttrAccounté para armazenar o nome da conta. Você pode armazenar coisas diferentes neles, mas isso pode ficar confuso.
Tammo Freese
9

Eu estava encontrando um bug outro dia (no iOS 7.1) que está relacionado a essa questão. Eu estava usando SecItemCopyMatchingpara ler um kSecClassGenericPassworditem e ele continuava retornando errSecItemNotFound(-25300) mesmo assim kSecAttrAccessGroup, kSecAttrAccounte kSecAttrServicetodos combinavam com o item no chaveiro.

Eventualmente eu descobri que kSecAttrAccessiblenão combinava. O valor no chaveiro continha pdmn = dk ( kSecAttrAccessibleAlways), mas eu estava usando kSecAttrAccessibleWhenUnlocked.

É claro que esse valor não é necessário em primeiro lugar para SecItemCopyMatching, mas OSStatusnão era errSecParamnem, errSecBadReqmas apenas errSecItemNotFound(-25300), o que o tornava um pouco complicado de encontrar.

Pois SecItemUpdateeu experimentei o mesmo problema, mas neste método, mesmo usando o mesmo kSecAttrAccessibleno queryparâmetro não funcionou. Apenas remover completamente esse atributo corrigiu isso.

Espero que este comentário economize alguns momentos preciosos de depuração para alguns de vocês.

izik Lisboa
fonte
4

A resposta dada por @Tammo Freese parece estar correta (mas não mencionando todas as chaves primárias). Eu estava procurando alguma prova na documentação. Finalmente encontrado:

Documentação da Apple mencionando as chaves primárias para cada classe de segredo (citação abaixo):

O sistema considera um item como uma duplicata de um determinado chaveiro quando esse chaveiro já possui um item da mesma classe com o mesmo conjunto de chaves primárias compostas. Cada classe de item de chaveiro possui um conjunto diferente de chaves primárias, embora alguns atributos sejam usados ​​em comum em todas as classes. Em particular, quando aplicável, kSecAttrSynchronizable e kSecAttrAccessGroup fazem parte do conjunto de chaves primárias . As chaves primárias adicionais por classe estão listadas abaixo:

  • Para senhas genéricas, as chaves primárias incluem kSecAttrAccount e kSecAttrService.
  • Para senhas de internet, as chaves primárias incluem kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort e kSecAttrPath.
  • Para certificados, as chaves primárias incluem kSecAttrCertificateType, kSecAttrIssuer e kSecAttrSerialNumber.
  • Para itens-chave, as chaves primárias incluem kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits e kSecAttrEffectiveKeySize.
  • Para itens de identidade, que são um certificado e uma chave privada agrupados, as chaves primárias são as mesmas de um certificado. Como uma chave privada pode ser certificada mais de uma vez, a exclusividade do certificado determina a identidade.
Julian Król
fonte
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar. - Da avaliação
pwc
concordou, embora, neste caso, significasse copiar todo o link.
Julian Król
0

Aqui está outra informação útil sobre a exclusividade de um item de chaveiro, encontrada na seção "Garantir capacidade de pesquisa" desta página de documentos da Apple .

Para ser capaz de encontrar o item mais tarde, você vai usar seu conhecimento de seus atributos. Neste exemplo, o servidor e a conta são as características distintivas do item. Para atributos constantes (aqui, o servidor), use o mesmo valor durante a pesquisa. Em contraste, o atributo da conta é dinâmico, porque contém um valor fornecido pelo usuário no tempo de execução. Contanto que seu aplicativo nunca adicione itens semelhantes com atributos variados (como senhas para contas diferentes no mesmo servidor), você pode omitir esses atributos dinâmicos como parâmetros de pesquisa e, em vez disso, recuperá-los junto com o item. Como resultado, ao pesquisar a senha, você também obtém o nome de usuário correspondente.

Se o seu aplicativo adicionar itens com atributos dinâmicos variados, você precisará encontrar uma maneira de escolher entre eles durante a recuperação. Uma opção é registrar informações sobre os itens de outra maneira. Por exemplo, se você mantém registros de usuários em um modelo de Dados Principais, armazena o nome de usuário lá depois de usar os serviços de chaveiro para armazenar o campo de senha. Posteriormente, você usa o nome de usuário obtido de seu modelo de dados para condicionar a busca pela senha.

Em outros casos, pode fazer sentido caracterizar melhor o item adicionando mais atributos. Por exemplo, você pode incluir o kSecAttrLabelatributo na consulta de adição original, fornecendo uma string que marca o item para uma finalidade específica. Então, você poderá usar este atributo para restringir sua pesquisa posteriormente.

O item da classe kSecClassInternetPasswordfoi usado no exemplo, mas há uma nota que diz:

Os serviços de chaveiro também oferecem a classe de item kSecClassGenericPassword relacionada. As senhas genéricas são semelhantes em muitos aspectos às senhas da Internet, mas carecem de certos atributos específicos para acesso remoto (por exemplo, não têm um atributo kSecAttrServer). Quando você não precisar desses atributos extras, use uma senha genérica.

Aleksandar
fonte