Что делает объект связки ключей уникальным (в iOS)?

Мой вопрос касается брелоков в iOS (iPhone, iPad,...). Я думаю (но не уверен), что реализация keychains под Mac OS X ставит один и тот же вопрос с тем же ответом.


iOS предоставляет пять типов (классов) элементов keychain. Вы должны выбрать одно из этих пяти значений для ключа kSecClass, чтобы определить тип:

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)

После долгого времени чтения документации яблок, блогов и записей в форуме я узнал, что элемент keychain типа kSecClassGenericPassword получает свою уникальность от атрибутов kSecAttrAccessGroup, kSecAttrAccount и kSecAttrService.

Если эти три атрибута в запросе 1 те же, что и в запросе 2, вы получаете тот же общий ключевой элемент пароля, независимо от каких-либо других атрибутов. Если один (или два или все) из этих атрибутов изменяет свое значение, вы получаете разные элементы.

Но kSecAttrService доступен только для элементов типа kSecClassGenericPassword, поэтому он не может быть частью "уникального ключа" элемента любого другого типа, и, похоже, нет документации, которая четко указывает который атрибуты однозначно определяют элемент keychain.

Пример кода в классе "KeychainItemWrapper" из "GenericKeychain" использует атрибут kSecAttrGeneric, чтобы сделать элемент уникальным, но это ошибка. Две записи в этом примере сохраняются только как две разные записи, потому что их kSecAttrAccessGroup отличается (у одного есть группа групп доступа, а другая освобождается). Если вы попытаетесь добавить второй пароль без группы доступа, используя Apple KeychainItemWrapper, вы потерпите неудачу.

Итак, пожалуйста, ответьте на мои вопросы:

  • Правда ли, что комбинация kSecAttrAccessGroup, kSecAttrAccount и kSecAttrService является "уникальным ключом" элемента keychain, kSecClass которого kSecClassGenericPassword?
  • Какие атрибуты делают элемент keychain уникальным, если его kSecClass не kSecClassGenericPassword?

Ответ 1

Первичные ключи следующие (полученные из файлов с открытым исходным кодом из Apple, см. Schema.m4, KeySchema.m4 и SecItem.cpp):

  • Для элемента keychain класса kSecClassGenericPassword первичный ключ представляет собой комбинацию kSecAttrAccount и kSecAttrService.
  • Для элемента keychain класса kSecClassInternetPassword первичный ключ представляет собой комбинацию kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort и kSecAttrPath.
  • Для элемента keychain класса kSecClassCertificate первичный ключ представляет собой комбинацию kSecAttrCertificateType, kSecAttrIssuer и kSecAttrSerialNumber.
  • Для элемента keychain класса kSecClassKey первичный ключ представляет собой комбинацию kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrEffectiveKeySize, и создатель, дата начала и дата окончания, которые еще не открываются SecItem.
  • Для элемента keychain класса kSecClassIdentity я не нашел информацию о полях первичного ключа в файлах с открытым исходным кодом, но поскольку идентификатор представляет собой комбинацию закрытого ключа и сертификата, я предполагаю, что первичный ключ комбинация полей первичного ключа для kSecClassKey и kSecClassCertificate.

Поскольку каждый элемент keychain принадлежит группе доступа к цепочке ключей, похоже, что группа доступа к keychain (поле kSecAttrAccessGroup) является добавленным полем ко всем этим основным ключам.

Ответ 2

Я наткнулся на ошибку на днях (на iOS 7.1), которая связана с этим вопросом. Я использовал SecItemCopyMatching для чтения элемента kSecClassGenericPassword, и он продолжал возвращать errSecItemNotFound (-25300), хотя kSecAttrAccessGroup, kSecAttrAccount и kSecAttrService все соответствовали элементу в цепочке ключей.

В конце концов я понял, что kSecAttrAccessible не соответствует. Значение в цепочке ключей содержало pdmn = dk (kSecAttrAccessibleAlways), но я использовал kSecAttrAccessibleWhenUnlocked.

Конечно, это значение не требуется в первую очередь для SecItemCopyMatching, но OSStatus не было errSecParam и errSecBadReq, а просто errSecItemNotFound (-25300), что сделало его несколько сложным, чтобы найти.

Для SecItemUpdate у меня возникла одна и та же проблема, но в этом методе даже с использованием того же kSecAttrAccessible в параметре query не работает. Исправлено только полное удаление этого атрибута.

Я надеюсь, что этот комментарий спасет несколько важных отладочных моментов для некоторых из вас.