Keychain — это защищённое хранилище учётных данных в iOS, watchOS, macOS и tvOS, которое управляет паролями, ключами и сертификатами на аппаратном уровне. В отличие от UserDefaults, данные в Keychain шифруются и изолируются по песочнице приложения. По данным Apple Developer Documentation, Keychain использует механизмы Secure Enclave и аппаратное шифрование AES-256 для защиты конфиденциальной информации.
Главное
Keychain — это защищённая база данных операционной системы Apple, предназначенная для безопасного хранения паролей, криптографических ключей, сертификатов и других конфиденциальных данных. Впервые Keychain появился в Mac OS 8.6 в 1999 году, а на iOS — с самой первой версии SDK.
Keychain решает фундаментальную проблему мобильной разработки: хранение секретов (токенов, паролей, ключей шифрования) в незащищённой файловой системе. В отличие от UserDefaults или SQLite, данные в Keychain хранятся в зашифрованном виде и автоматически шифруются при записи на диск.
Каждое приложение iOS работает в изолированной песочнице и имеет доступ только к своему keychain. Однако разработчик может настроить общий доступ к Keychain между приложениями одного разработчика через keychain access groups. Пользователь iCloud Keychain дополнительно синхронизирует данные между устройствами через сквозное шифрование.
По данным Apple Security White Paper (2025), Keychain использует аппаратное шифрование AES-256 с ключом, привязанным к уникальному идентификатору устройства (UID), что делает данные недоступными при извлечении из устройства.
Keychain функционирует как сервис уровней: приложение обращается к Security framework, который взаимодействует с демоном securityd, управляющим зашифрованной SQLite-базой Keychain. Каждая запись шифруется отдельным ключом, а сами ключи защищены аппаратным модулем Secure Enclave на устройствах с чипом A7 и новее.
Security framework предоставляет C-интерфейс для работы с Keychain Services. Основные функции: SecItemAdd (добавление), SecItemCopyMatching (поиск), SecItemUpdate (обновление) и SecItemDelete (удаление). Каждая операция принимает словарь атрибутов CFDictionaryRef, описывающий тип запроса.
Данные классифицируются по классам: kSecClassGenericPassword (произвольный пароль), kSecClassInternetPassword (пароль к интернет-сервису), kSecClassCertificate (сертификат), kSecClassKey (криптографический ключ) и kSecClassIdentity (связка ключа и сертификата).
При сохранении пароля приложение формирует словарь с атрибутами: сервис, учётная запись, уровень доступа, метка. Security framework передаёт данные демону securityd, который шифрует запись ключом, привязанным к устройству, и сохраняет в Keychain SQLite. Извлечение происходит аналогично: запрос по атрибутам, поиск, расшифровка и возврат данных.
Важная особенность: при удалении приложения iOS автоматически очищает Keychain записи, созданные этим приложением (с iOS 10.3+). При переустановке приложения Keychain-данные предыдущей инсталляции недоступны, если не использовался iCloud Keychain.
import Security
func savePassword(service: String, account: String, password: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecValueData as String: password,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
SecItemAdd(query as CFDictionary, nil)
}
Keychain поддерживает пять основных классов данных, каждый из которых имеет свой набор атрибутов и область применения. От выбора класса зависит, какие ключи поиска доступны и как данные шифруются.
Классы kSecClassGenericPassword и kSecClassInternetPassword предназначены для хранения строковых паролей. Internet-версия дополнительно хранит данные сервера: домен, протокол, порт и путь аутентификации. GenericPassword используется для хранения любых паролей без привязки к серверу.
При сохранении интернет-пароля можно указать kSecAttrServer (домен), kSecAttrProtocol (HTTPS, FTP и т.д.), kSecAttrPort и kSecAttrPath. Это позволяет точно идентифицировать запись при автоматическом заполнении через AutoFill.
Класс kSecClassKey хранит симметричные (AES) и асимметричные (RSA, EC) ключи. Класс kSecClassCertificate — сертификаты X.509. Класс kSecClassIdentity объединяет закрытый ключ и соответствующий сертификат в одну запись.
Для асимметричных ключей Keychain поддерживает атрибуты: kSecAttrKeyType (RSA, EC), kSecAttrKeySizeInBits (2048, 256), kSecAttrIsPermanent (сохранять ли в Keychain). Для сертификатов: kSecAttrLabel (человекочитаемое имя), kSecAttrSubject (DN владельца), kSecAttrIssuer (DN издателя).
| Класс Keychain | Назначение | Тип данных |
|---|---|---|
| kSecClassGenericPassword | Пароль приложения | Data (NSString) |
| kSecClassInternetPassword | Пароль к серверу | Data + атрибуты сервера |
| kSecClassCertificate | Сертификат X.509 | SecCertificate |
| kSecClassKey | Криптографический ключ | SecKey |
| kSecClassIdentity | Ключ + сертификат | SecIdentity |
Основной интерфейс взаимодействия с Keychain в нативном коде iOS — функции Security framework. Для Swift и Objective-C доступен один C-интерфейс, обёрнутый в удобные функции.
Функция SecItemAdd добавляет новый элемент в Keychain. На вход принимает словарь с классом, данными и атрибутами. Возвращает статус errSecSuccess (0) или код ошибки. Функция SecItemCopyMatching выполняет поиск по атрибутам и возвращает найденные данные.
Для поиска используется флаг kSecReturnData — если true, функция возвращает содержимое записи. Флаг kSecMatchLimit определяет количество результатов: kSecMatchLimitOne (один) или kSecMatchLimitAll (все).
func readPassword(service: String, account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else { return nil }
return item as? Data
}
SecItemUpdate позволяет изменить атрибуты или данные существующей записи. SecItemDelete удаляет все записи, соответствующие переданному словарю. При удалении важно указать достаточное количество атрибутов, чтобы случайно не удалить чужие записи в shared keychain.
func deletePassword(service: String, account: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
SecItemDelete(query as CFDictionary)
}
Каждая запись Keychain имеет атрибут kSecAttrAccessible, который определяет, когда данные доступны для чтения. Это критически важно для безопасности: неправильный уровень доступа может сделать данные уязвимыми или, наоборот, недоступными в нужный момент.
kSecAttrAccessibleWhenUnlocked — данные доступны только когда устройство разблокировано. kSecAttrAccessibleAfterFirstUnlock — доступны после первой разблокировки после перезагрузки. kSecAttrAccessibleAlways — доступны всегда (не рекомендуется). Постфикс ThisDeviceOnly запрещает перенос данных через резервное копирование.
На устройствах с Secure Enclave ключ шифрования Keychain может быть дополнительно защищён биометрией: kSecAccessControlBiometryCurrentSet или kSecAccessControlUserPresence. Это добавляет требование Face ID или Touch ID для каждой операции чтения.
iCloud Keychain синхронизирует записи между устройствами Apple через сквозное шифрование (end-to-end). Apple не имеет доступа к расшифрованным данным. Для включения синхронизации добавьте атрибут kSecAttrSynchronizable со значением kCFBooleanTrue.
Важно: синхронизируемые записи не могут содержать атрибут ThisDeviceOnly, так как они должны быть доступны на других устройствах. При потере всех устройств iCloud Keychain восстанавливается через код восстановления или резервный ключ iCloud.
Для токенов аутентификации используйте kSecAttrAccessibleWhenUnlockedThisDeviceOnly — данные доступны только при разблокированном устройстве и не копируются в бэкап. Для сертификатов, которые должны быть доступны фоновым задачам, используйте kSecAttrAccessibleAfterFirstUnlock. Never используйте kSecAttrAccessibleAlways для конфиденциальных данных.
Рекомендуется всегда добавлять постфикс ThisDeviceOnly для данных, не требующих синхронизации. Это предотвращает случайное копирование secrets в iCloud или iTunes backup, где они могут быть извлечены инструментами анализа. Для критических данных комбинируйте доступность с kSecAccessControlUserPresence — это добавляет требование биометрии или кода для расшифровки записи.
При использовании iCloud Keychain учитывайте, что синхронизируемые записи доступны на всех устройствах пользователя: если злоумышленник получит доступ к одному устройству, данные могут быть скомпрометированы на всех. Для изолированных secrets используйте ThisDeviceOnly.
Часто задаваемые вопросы
UserDefaults хранит данные в открытом виде в файле .plist, доступном при анализе IPA. Keychain шифрует каждую запись отдельно, использует аппаратное шифрование и поддерживает уровни доступа, биометрию и синхронизацию через iCloud.
При каждом запуске на симуляторе iOS Keychain очищается. На реальном устройстве удалите приложение — iOS удалит все Keychain-записи этого разработчика. Для выборочной очистки вызовите SecItemDelete с соответствующими атрибутами.
Да, через keychain access groups. Приложения должны иметь одинаковый team ID в подписи и entitlements c одинаковым keychain-access-groups. Разработчик настраивает общий доступ в Xcode Capabilities.
Apple не документирует строгий лимит, но на практике рекомендуется не превышать 1–4 КБ на запись. Для больших объёмов используйте шифрование файлов и хранение ключа в Keychain, а сами данные — в Documents.
При джейлбрейке защита Keychain снижается, так как злоумышленник получает root-доступ и может читать Keychain SQLite напрямую. Для критических данных используйте дополнительное шифрование с ключом, полученным с сервера.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также