- Что такое сертификат?
- Add certificate to ios keychain in swift
- Ecdsa
- Keychain — связка ключей
- Keystore
- Видео
- Генерирование нового случайного ключа
- Диспетчер учётных записей
- Зашифровать закрытый ключ
- Извлечь только сертификаты или закрытый ключ
- Использование асимметричных ключей rsa для старых устройств
- Конвертировать закрытый ключ в формат pkcs # 1
- Отключаем автоматическое закрытие связки ключей
- Отключаем и повторно подключаем cвязку ключей в icloud
- Проверяем и исправляем связку ключей
- Просмотр информации pkcs # 12 на экране
- Расшифровка в массив байтов
- Сброс связки ключей.
- Смотрим на примере
- Сохранить сертификаты и личные ключи в файлы
- Функции защиты класса для данных связки ключей
- Хранение данных в связке ключей
- Шаг 1 – храните ключ в связке ключей
- Шифрование данных
- Заключение
Что такое сертификат?

Сертификат — это вложение в электронный документ, которое обеспечивает безопасную передачу информации через интернет. Сертификаты используются веб-браузерами и приложениями для обмена электронной почтой и текстовыми сообщениями.
Во время связи с безопасным сайтом обмениваемая с сайтом информация является зашифрованной. Шифрование защищает информацию входа, номера кредитных карточек, адресов и другую конфиденциальную информацию.
В macOS сертификаты являются частью цифровой идентификации и хранятся в связке ключей. Приложение «Связка ключей» помогает управлять связками ключей и сертификатами.
Открыть приложение «Связка ключей»
Сертификаты выдаются доверенными организациями, например корпорациями VeriSign, Inc. или RSA Data Security, Inc. При открытии защищенного веб-сайта система macOS проверяет сертификат сайта по списку надежных сертификатов. Если сертификат веб-сайта не распознан или сайт не имеет сертификата, Вы получите сообщение.
Подлинность сертификата может проверяться электронно с помощью «инфраструктуры открытого ключа» или PKI. Сертификаты состоят из Вашего открытого ключа, идентификации организации, бюро сертификации (БС), которое подписало сертификат, и других данных, связанных с идентификацией.
Обычно сертификат имеет специальное предназначение — например, используется в цифровых подписях, при шифровании или на веб-серверах. Это называется ограничением использования ключа. Можно создать один универсальный сертификат для нескольких целей, но не рекомендуется создавать сертификат, пригодный для любых целей. Универсальный сертификат менее безопасен.
Сертификат действителен только на протяжении определенного срока, по истечении которого сертификат становится недействительным и должен быть заменен более новой версией. Также бюро сертификации может отозвать сертификат, сделав его недействительным до истечения указанного срока.
Если Вам необходимо отправить кому-либо сертификат, Вы можете экспортировать сертификат с помощью приложения «Связка ключей» и переслать его по электронной почте или любым другим способом. Аналогично, если кто-то прислал Вам сертификат, Вы можете добавить его в связку ключей, перетянув на значок «Связка ключей» или воспользовавшись меню «Импорт» данного приложения.
Add certificate to ios keychain in swift
im trying to write a root certificate to my apps keychain so that I can talk to a server serving up a self signed cert.
class func setCertificate(certData: NSData, forKey keyName: String) -> Bool
{
var secCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)
var keychainQueryDictionary: NSMutableDictionary = self.setupKeychainQueryDictionaryForKey(keyName)
keychainQueryDictionary[kSecClassCertificate as NSString] = secCert.takeRetainedValue()
// Protect the keychain entry so it's only valid when the device is unlocked
keychainQueryDictionary[SecAttrAccessible] = kSecAttrAccessibleWhenUnlocked
// Disable icloud sync of keychain data
keychainQueryDictionary[kSecAttrSynchronizable as NSString] = kCFBooleanFalse
let status: OSStatus = SecItemAdd(keychainQueryDictionary, nil)
println(status)
if status == errSecSuccess
{
return true
}
return false
}
But the OSStatus returned is -50 (One or more parameters passed to a function where not valid.), i’ve tried numerous casts etc but not getting anywhere. The certificate is definitely valid as SecCertificateCreateWithData would return nil if there was an issue with its format.
This is my function for setting up the keychain query
private class func setupKeychainQueryDictionaryForKey(keyName: String) -> NSMutableDictionary
{
// Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
var keychainQueryDictionary: NSMutableDictionary = [SecClass:kSecClassGenericPassword]
// Uniquely identify this keychain accessor
keychainQueryDictionary[SecAttrService] = KeychainManager.serviceName
// Uniquely identify the account who will be accessing the keychain
var encodedIdentifier: NSData? = keyName.dataUsingEncoding(NSUTF8StringEncoding)
keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
keychainQueryDictionary[SecAttrAccount] = encodedIdentifier
return keychainQueryDictionary
}
Has anyone done this in swift or any advice from anyone?
Cheers
Ecdsa
openssl pkcs12 -in INFILE.p12 -nodes -nocerts | openssl ec -out OUTFILE.key
Keychain — связка ключей
Представленный в Android 4.0 (API Level 14), Keychain API управлял ключами. В частности, это работает с объектами PrivateKey и X509Certificate и обеспечивает более безопасный контейнер, чем использование хранилища данных вашего приложения.
Связано это с тем, что разрешения закрытых ключей открывают доступ к ключам только вашему приложению и только после авторизации пользователя. Это означает, что, прежде чем, вы сможете использовать хранилище учётных данных, на устройстве должен быть настроен экран блокировки. Кроме того, объекты в связке ключей можно объединить с защитой от оборудования, если доступно.
Код установки сертификата выглядит следующим образом:
Intent intent = KeyChain.createInstallIntent(); byte[] p12Bytes = //... read from file, such as example.pfx or example.p12... intent.putExtra(KeyChain.EXTRA_PKCS12, p12Bytes); startActivity(intent);
Пользователю будет предложено ввести пароль для доступа к закрытому ключу и указать имя сертификата. Для получения ключа, в следующем коде представлен пользовательский интерфейс, который позволяет пользователю выбирать ключ из списка установленных ключей.
Keystore
В предыдущем уроке, мы рассмотрели защиту данных с помощью предоставляемого пользователем пароля. Такой вариант хорош, но требования к приложениям часто уводят от того, чтобы пользователи каждый раз входили в систему и запоминали дополнительный пароль.
Вот где можно использовать KeyStore API. Начиная с API 1, KeyStore используется системой для хранения учётных данных WiFi и VPN. Начиная с 4.3 (API 18), вы можете работать с асимметричными ключами конкретного приложения, а в Android M (API 23) можно хранить симметричный ключ AES.
Преимущество хранения ключа в хранилище ключей заключается в том, что он позволяет работать с ключами без раскрытия секретного содержимого этого ключа; данным ключа не место в приложении. Помните, что ключи защищаются разрешениями, так что только ваше приложение может получить к ним доступ, и они могут быть дополнительно защищены аппаратным обеспечением, если устройство поддерживает это. Создаётся контейнер, который усложняет извлечение ключей с устройства.
Видео
Во всех приведенных ниже примерах подставьте имена файлов, с которыми вы фактически работаете.INFILE.p12, OUTFILE.crtи OUTFILE.key.
Генерирование нового случайного ключа
В этом примере вместо генерации ключа AES из предоставленного пользователем пароля мы можем автоматически сгенерировать случайный ключ, который будет защищён в хранилище ключей KeyStore. Мы можем сделать это, создав экземпляр KeyGenerator, настроенного на поставщика “AndroidKeyStore”.
Диспетчер учётных записей
Диспетчер учётных записей (Account Manager) — это централизованный помощник для работы с учётными данными пользователя, поэтому вашему приложению, иметь дело с паролями напрямую не нужно. Часто он предоставляет токен вместо реального имени пользователя и пароля, который можно использовать для выполнения аутентифицированных запросов к службе. Например, при запросе токена OAuth2.
Иногда, вся необходимая информация уже хранится на устройстве, а иногда Account Manager придётся обращаться к серверу за новым токеном. Вы, наверное, видели раздел Учётные записи в Настройках вашего устройства для разных приложений. И вот как, мы можем получить список доступных учётных записей:
AccountManager accountManager = AccountManager.get(this); Account[] accounts = accountManager.getAccounts();
Этому коду потребуется разрешение android.permission.GET_ACCOUNTS. Если вы ищете определённую учётную запись, вы можете найти её вот так:
AccountManager accountManager = AccountManager.get(this);
Account[] accounts = accountManager.getAccountsByType("com.google");Зашифровать закрытый ключ
Если вы хотите зашифровать закрытый ключ и защитить его паролем перед выводом, просто опустите -nodes флаг из команды:
openssl pkcs12 -info -in INFILE.p12
В этом случае вам будет предложено ввести и проверить новый пароль после того, как OpenSSL выдаст какие-либо сертификаты, а закрытый ключ будет зашифрован (обратите внимание, что текст ключа начинается с —–BEGIN ENCRYPTED PRIVATE KEY—–):
Введите PEM фразу: контролирующая - Enter PEM фразу: ----- BEGIN зашифрованного Частного KEY ----- MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIGwhJIMXRiLQCAggA MBQGCCqGSIb3DQMHBAiXdeymTYuedgSCBMjwGg78PsqiNJLfpDFbMxL98u3tK9Cs ... SGVCCBj5vBpSbBXAGbOv74h4satKmAMgGc8SgU06geS9gFgt / wLwehMJ / H4BSmex 4S / 2tYzZrDBJkfH9JpggubYRTgwfAGY2BkX03dK2sqfu QVTVTKMj2VI0sKcFfLZ BDW = ----- END зашифрованы ЧАСТНЫЙ КЛЮЧ -----
Извлечь только сертификаты или закрытый ключ
Если вы хотите вывести только закрытый ключ, добавьте -nocerts в команду:
openssl pkcs12 -info -in INFILE.p12 -nodes -nocerts
Если вам нужны только сертификаты, используйте -nokeys (и поскольку нас не интересует закрытый ключ, мы также можем спокойно опустить -nodes):
openssl pkcs12 -info -in INFILE.p12 -nokeys
Использование асимметричных ключей rsa для старых устройств
Это хорошее решение для хранения данных в версии M и выше, но что, если ваше приложение поддерживает более ранние версии? Хотя симметричные ключи AES не поддерживаются в M, поддерживаются асимметричные ключи RSA. Это означает, что для достижения того же результата, мы можем использовать RSA ключи и шифрование.
Основное отличие заключается в том, что асимметричная пара ключей содержит два ключа: закрытый и открытый ключ, где открытый ключ шифрует данные, а закрытый ключ расшифровывает их. KeyPairGeneratorSpec передаётся в KeyPairGenerator, который инициализируется с помощью KEY_ALGORITHM_RSAи поставщика AndroidKeyStore.
private void testPreMEncryption()
{
try
{
//Generate a keypair and store it in the KeyStore
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 10);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias("MyKeyAlias")
.setSubject(new X500Principal("CN=MyKeyName, O=Android Authority"))
.setSerialNumber(new BigInteger(1024, new Random()))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setEncryptionRequired() //on API level 18, encrypted at rest, requires lock screen to be set up, changing lock screen removes key
.build();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyPairGenerator.initialize(spec);
keyPairGenerator.generateKeyPair();
//Encryption test
final byte[] encryptedBytes = rsaEncrypt("My secret string!".getBytes("UTF-8"));
final byte[] decryptedBytes = rsaDecrypt(encryptedBytes);
final String decryptedString = new String(decryptedBytes, "UTF-8");
Log.e("MyApp", "Decrypted string is " decryptedString);
}
catch (Throwable e)
{
e.printStackTrace();
}
}Для шифрования, из пары ключей мы получаем RSAPublicKey и используем его с объектом Cipher.
public byte[] rsaEncrypt(final byte[] decryptedBytes)
{
byte[] encryptedBytes = null;
try
{
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("MyKeyAlias", null);
final RSAPublicKey publicKey = (RSAPublicKey)privateKeyEntry.getCertificate().getPublicKey();
final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
cipherOutputStream.write(decryptedBytes);
cipherOutputStream.close();
encryptedBytes = outputStream.toByteArray();
}
catch (Throwable e)
{
e.printStackTrace();
}
return encryptedBytes;
}Расшифровка выполняется с использованием объекта RSAPrivateKey.
public byte[] rsaDecrypt(final byte[] encryptedBytes)
{
byte[] decryptedBytes = null;
try
{
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("MyKeyAlias", null);
final RSAPrivateKey privateKey = (RSAPrivateKey)privateKeyEntry.getPrivateKey();
final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
final CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(encryptedBytes), cipher);
final ArrayList<Byte> arrayList = new ArrayList<>();
int nextByte;
while ( (nextByte = cipherInputStream.read()) != -1 )
{
arrayList.add((byte)nextByte);
}
decryptedBytes = new byte[arrayList.size()];
for(int i = 0; i < decryptedBytes.length; i )
{
decryptedBytes[i] = arrayList.get(i);
}
}
catch (Throwable e)
{
e.printStackTrace();
}
return decryptedBytes;
}Кое-что об RSA — шифрование медленнее, чем в AES. Для небольших объёмов информации, например, когда вы защищаете строки общих настроек, это не страшно. Если вы обнаружите проблему с производительностью при шифровании больших объёмов данных, то вместо этого вы можете использовать данный пример для шифрования и хранения только ключа AES.
И тогда, для остальной части ваших данных, используйте более быстрое шифрование AES, которое обсуждалось в предыдущем уроке. Вы можете сгенерировать новый AES ключ и преобразовать его в массив byte[], который совместим с этим примером.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256); //AES-256
SecretKey secretKey = keyGenerator.generateKey();
byte[] keyBytes = secretKey.getEncoded();Чтобы получить ключ, сделайте вот так:
SecretKey key = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
Довольно много кода! Для простоты примеров, я пропустил обработку исключений. Но помните, что для итогового кода не рекомендуется просто перехватывать все случаи Throwable в одном операторе catch.
Конвертировать закрытый ключ в формат pkcs # 1
Приведенные выше примеры выводят закрытый ключ в OpenSSL по умолчанию. PKCS # 8 формат. Если вы знаете, что вам нужно PKCS # 1 вместо этого вы можете передать вывод утилиты OpenSSL PKCS # 12 ее утилите RSA или EC в зависимости от типа ключа. Обе команды ниже выведут файл ключа в формате PKCS # 1:
Отключаем автоматическое закрытие связки ключей
В целях безопасности, Связка ключей может блокироваться после определенного периода времени без активности или при переходе мака в спящий режим. Стоит отличать эту функцию от закрытия экрана. При закрытии связки ключей работать с компьютером можно и без ввода пароля, а при закрытия экрана, без пароля пользователь не сможет снова получить доступ к системе. Изменить настройки этой функции довольно просто.
- Откройте Связку ключей из папки «Утилиты»
- Щелкните правой клавишей по связке «Вход» и выберите пункт «Изменить параметры для связки ключей «Вход»»
- В открывшемся окне снимите галочки с двух доступных пунктов, если хотите отключить функцию автоматического закрытия, либо настройте её так, как вам удобно.

Также может оказаться полезным включить отображение Связки ключей на вашей панели меню. Для этого откройте меню Связка ключей – Настройка – Основные и поставьте галочку напротив пункта «Показывать статус связки ключей в строке меню».
Отключаем и повторно подключаем cвязку ключей в icloud
Если вы используете связку ключей iCloud, чтобы иметь к ней доступ на нескольких своих устройствах, стоит попробовать отключить эту функцию на маке, а затем повторно её включить. Перед тем как вы это сделаете, настоятельно рекомендуем убедиться, что у вас есть полная и актуальная резервная копия системы!
Перейдите в меню Системные настройки – iCloud. Снимите галочку с пункта «Связка ключей», подтвердите, что действительно хотите отключить эту функцию, а затем снова поставьте галочку. Таким образом вы удалите связку ключей со своего компьютера, а затем снова её добавите, что с большой вероятность может исправить возникшую проблему.
Проверяем и исправляем связку ключей
Если ваши связки ключей не работают должным образом, их можно исправить, используя функцию Первая помощь. Но перед этим стоит убедиться, что она настроена должным образом.
- Откройте меню Связка ключей - Настройка и выберите вкладку «Первая помощь».
- Убедитесь, что последние три пункта отмечены галочками, после чего закройте это окно.

- Перейдите в меню Связка ключей - Первая помощь Связки ключей.
- Введите имя и пароль своей учётной записи, выберите пункт «Проверка» и нажмите кнопку «Старт».
- Если были найдены ошибки, в том же окне выберите пункт «Восстановление» и снова нажмите кнопку «Старт».

Просмотр информации pkcs # 12 на экране
Чтобы вывести всю информацию из файла PKCS # 12 на экран в Формат PEM, используйте эту команду:
openssl pkcs12 -info -in INFILE.p12 -nodes
Затем вам будет предложено ввести пароль файла PKCS # 12:
Введите пароль для импорта:
Введите пароль, введенный при создании файла PKCS # 12, и нажмите enter. OpenSSL выведет на экран все сертификаты и закрытые ключи из файла:
Расшифровка в массив байтов
Для расшифровки применяется обратный ход. Объект Cipher инициализируется с использованием константы DECRYPT_MODE, и возвращается расшифрованный массив byte[].
private byte[] decrypt(final HashMap<String, byte[]> map)
{
byte[] decryptedBytes = null;
try
{
//Get the key
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry)keyStore.getEntry("MyKeyAlias", null);
final SecretKey secretKey = secretKeyEntry.getSecretKey();
//Extract info from map
final byte[] encryptedBytes = map.get("encrypted");
final byte[] ivBytes = map.get("iv");
//Decrypt data
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec spec = new GCMParameterSpec(128, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
decryptedBytes = cipher.doFinal(encryptedBytes);
}
catch (Throwable e)
{
e.printStackTrace();
}
return decryptedBytes;
}Сброс связки ключей.
Если ничего из описанного выше не помогло, можно попробовать сбросить связку ключей, получив в результате новый, чистый файл. Сделав это вы все ещё будете иметь доступ к старому файлу связки, так что ваши пароли не будут утеряны, но система их использовать не будет, и вводить их придется заново. Для того чтобы сбросить связку ключей откройте меню Связка ключей - Настройка - Основные, нажмите кнопку «Восстановить связку по умолчанию», подтвердите свой выбор и дождитесь окончания процесса.
После этого, если вам понадобится пароль, сохраненный в старой связке, вы можете открыть её и перенести нужный вам элемент из списка в новую связку, или открыть этот элемент двойным щелчком мыши и посмотреть пароль, поставив галочку напротив пункта «Показать пароль».
Огромное спасибо Кристоферу Кесслеру за материал, послуживший основой для написания этой статьи.
Смотрим на примере
Теперь мы можем посмотреть на пример!
Сохранить сертификаты и личные ключи в файлы
Вы можете экспортировать сертификаты и закрытый ключ из файла PKCS # 12 и сохранить их в формате PEM в новый файл, указав выходное имя файла:
openssl pkcs12 -in INFILE.p12 -out OUTFILE.crt -nodes
Вам снова будет предложено ввести пароль файла PKCS # 12. Как и раньше, вы можете зашифровать закрытый ключ, удалив -nodes пометка из команды и / или добавление -nocerts or -nokeys выводить только закрытый ключ или сертификаты. Итак, для генерации файла закрытого ключа мы можем использовать эту команду:
openssl pkcs12 -in INFILE.p12 -out OUTFILE.key -nodes -nocerts
И чтобы создать файл, включающий только сертификаты, используйте это:
openssl pkcs12 -in INFILE.p12 -out OUTFILE.crt -nokeys
Функции защиты класса для данных связки ключей
К элементам связки ключей применяются функции защиты следующих классов.
Элемент | Доступность |
|---|---|
Пароли Wi-Fi | После первой разблокировки |
Учетные записи Почты | После первой разблокировки |
Учетные записи Microsoft Exchange ActiveSync | После первой разблокировки |
Пароли VPN | После первой разблокировки |
LDAP, CalDAV, CardDAV | После первой разблокировки |
Токены учетных записей социальных сетей | После первой разблокировки |
Ключи шифрования уведомлений Handoff | После первой разблокировки |
Токен iCloud | После первой разблокировки |
Ключи iMessage | После первой разблокировки |
Пароль «Домашней коллекции» | В разблокированном состоянии |
Пароли Safari | В разблокированном состоянии |
Закладки Safari | В разблокированном состоянии |
Резервное копирование с помощью Finder или iTunes | В разблокированном состоянии, без возможности переноса |
Сертификаты VPN | Всегда, без возможности переноса |
Ключи Bluetooth® | Всегда, без возможности переноса |
Токен службы Apple Push Notification (APNs) | Всегда, без возможности переноса |
Сертификаты и личный ключ iCloud | Всегда, без возможности переноса |
Сертификаты и личные ключи, установленные профилем конфигурации | Всегда, без возможности переноса |
PIN-код SIM-карты | Всегда, без возможности переноса |
Токен Локатора | Всегда |
Автоответчик | Всегда |
Хранение данных в связке ключей
Связка ключей – зашифрованная (используется 128-битный шифр AES) централизованная база данных Sqlite, в которой хранятся учетные данные пользователей для множества приложений и сетевых сервисов с ограниченными правами доступа. На iPhone связка ключей используется для хранения небольших объемов конфиденциальных данных вроде имен пользователей, паролей, ключей шифрования, сертификатов и закрытых ключей.
Как правило, приложения хранят учетные данные пользователей в связке ключей, чтобы обеспечить прозрачную аутентификацию и не спрашивать эти данные у пользователя при каждом запуске. Для чтения и модификации данных связки ключей приложения iOS используют API-вызовы сервисной библиотеки (методы secItemAdd, secItemDelete, secItemCopyMatching и secItemUpdate).
Разработчики применяют данный API, чтобы указать системе, что конфиденциальные данные должны храниться в связке ключей, а не в списке свойств или текстовом файле конфигурации. Местоположение БД связки ключей в файловой системе: /private/var/Keychains/keychain-2.db.
Связка ключей содержит некоторое количество элементов, и каждый элемент содержит зашифрованные данные и набор незашифрованных атрибутов, которые описывают эти данные. Набор атрибутов, связанных с элементом связки ключей, зависит от типа элемента (kSecClass).
В iOS элементы связки ключей разбиваются на 5 классов – пароли общего назначения (kSecClassGenericPassword), пароли для Интернет-приложений (kSecClassInternetPassword), сертификаты (kSecClassCertificate), ключи (kSecClassKey) и цифровые “личности” (digital identity)
(kSecClassIdentity, цифровая личность = сертификат ключ). Все элементы связки ключей iOS хранятся в четырех таблицах: genp, inet, cert и keys. Таблица genp содержит пароли общего назначения, таблица inet – пароли Интернет-приложений, а cert & keys содержат сертификаты, ключи и цифровые личности.
Таблицы связки ключей
База данных связки ключей зашифрована аппаратным ключом, уникальным для каждого устройства. Аппаратный ключ не может быть извлечен из устройства, поэтому данные, хранимые в связке ключей, доступны только на исходном устройстве и не могут быть перемещены на любое другое.
Формат файла связки ключей
Элементы связки ключей, созданные различными приложениями, отделены друг от друга: одно приложение не может обратиться к данным связки ключей, связанным с другим приложением. Данные, хранимые приложением в связке ключей, также отделены от песочницы приложения.
Разграничение доступа приложений к связке ключей осуществляется за счет системного процесса securityd. Права доступа приложения к элементам связки ключей хранятся в виде пар ключ-значение, называемых entitlements. Службы связки ключей используют данную информацию для выдачи приложениям разрешений на доступ к их собственным элементам связки ключей.
Права приложения определяют его доступ к связке ключей, некоторым механизмам iOS вроде push-уведомлений, возможность взаимодействия с сервисом iCloud и т. д. Права приложения указывают на особые возможности или разрешения безопасности. Файл, описывающий права приложения, которое хранит данные в связке ключей, содержит идентификатор приложения (application-identifier) и может содержать несколько констант keychain-access-groups.
Каждое приложение iOS наделяется уникальным идентификатором. На основе этих идентификаторов служба связки ключей разграничивает доступ к элементам связки. По умолчанию приложения могут обращаться только к данным, связанным с их собственным идентификатором.
Для того, чтобы к элементу связки могли обращаться несколько приложений, существуют группы доступа к связке ключей (keychain-access-groups). Приложения, в правах которых прописана одна и та же группа доступа, могут разделять доступ к элементам связки ключей.
Чтобы получить список прав приложения iOS, соединитесь с iPhone по SSH, перейдите в домашнюю директорию приложения (/var/mobile/Applications/[unique-id]/) и запустите указанную ниже команду.
sed -n '/<dict>/,/</dict>/p' [AppDirectory]/[ApplicationBinary]
Шаг 1 – храните ключ в связке ключей
Просто сделайте это один раз:
ssh-add -K ~/.ssh/[your-private-key]
Введите ключевую фразу-пароль, и вас больше не попросят.
(Если вы используете версию OSX, предшествующую Sierra, то все готово, шаг 2 не требуется.)
Шифрование данных
Теперь, когда ключ хранится в хранилище KeyStore, мы можем создать метод, который зашифрует данные с использованием объекта Cipher, учитывая SecretKey. Это вернёт HashMap, содержащий зашифрованные данные и случайный ВИ, который понадобится для расшифровки данных. Зашифрованные данные вместе с ВИ могут быть сохранены в файл или в открытых настройках.
private HashMap<String, byte[]> encrypt(final byte[] decryptedBytes)
{
final HashMap<String, byte[]> map = new HashMap<String, byte[]>();
try
{
//Get the key
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry)keyStore.getEntry("MyKeyAlias", null);
final SecretKey secretKey = secretKeyEntry.getSecretKey();
//Encrypt data
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final byte[] ivBytes = cipher.getIV();
final byte[] encryptedBytes = cipher.doFinal(decryptedBytes);
map.put("iv", ivBytes);
map.put("encrypted", encryptedBytes);
}
catch (Throwable e)
{
e.printStackTrace();
}
return map;
}Заключение
На этом урок по работе с учётными данными и ключами завершён. Большая часть неразберихи вокруг ключей и хранилища связана с эволюцией ОС Android, но вы можете выбрать, какое решение использовать, учитывая уровень API, поддерживаемый вашим приложением.
Теперь, когда мы рассмотрели лучшие примеры защиты данных в состоянии покоя, следующий урок будет сосредоточен на защите данных при передаче.
