iOS. Приемы программирования Нахавандипур Вандад

4. Выберите сертификат (-ы) разработки, с которыми хотите связать ваш профиль. Затем нажмите кнопку Continue (Продолжить).

5. Выберите список устройств, на которые можно будет установить ваш профиль (это делается только для профилей Development и Ad Hoc, но не для App Store) и нажмите кнопку Continue (Продолжить).

6. На следующем экране система потребует от вас указать имя вашего профиля. Введите имя, соответствующее правилам из Security App Dev Profile, а потом нажмите кнопку Generate (Сгенерировать), чтобы создать профиль инициализации.

7. Теперь ваш профиль готов к загрузке (рис. 8.6). Нажмите кнопку Download (Загрузить), чтобы загрузить профиль.

Рис. 8.6. Профиль для разработки сгенерирован и готов к загрузке

8. Для установки профиля перетащите загруженный профиль в iTunes. В результате профиль с его оригинальным именем будет установлен в каталоге ~/Library/MobileDevice/Provisioning Profiles/. Мне известно, что многие iOS-разработчики устанавливают профиль инициализации, просто дважды щелкнув на нем кнопкой мыши. Такой способ частично работает, то есть при двойном щелчке профиль действительно устанавливается в упомянутом каталоге. Но оригинальное имя профиля при этом стирается и заменяется SHA1-хешем профиля. Если позже вы откроете каталог, то не сможете понять, какой профиль вам нужен. Придется просматривать все профили и выяснять их имена. Поэтому я настоятельно не рекомендую устанавливать профили двойным щелчком кнопкой мыши. Лучше перетаскивать их в iTunes или вручную вставлять в данный каталог.

Блестяще. Вы установили на компьютере профиль инициализации для вашего приложения. Воспользуйтесь настройками сборки проекта, чтобы убедиться в том, что выбран правильный профиль для схемы Debug (Отладка). Затем повторите описанный процесс при создании профилей Ad Hoc и App Store, чтобы гарантировать сборку приложения с нужными профилями для схемы Release (Выпуск).

Созданный вами профиль инициализации обеспечивает отладку ваших приложений на устройстве с iOS и удобное сохранение данных на диске с применением связки ключей.

См. также

Раздел 8.0.

8.2. Хранение значений в связке ключей

Постановка задачи

Требуется обеспечить безопасное хранение конфиденциальных данных в связке ключей.

Решение

Необходимо гарантировать, что ваше приложение будет скомпоновано с учетом требований фреймворка Security (Безопасность). Затем воспользуйтесь функцией SecItemAdd для добавления нового элемента в связку ключей приложения.

Обсуждение

API связки ключей в операционных системах iOS и OS X написаны на языке C. Таким образом, у нас нет мостика к Objective-C или какого-то промежуточного уровня, который предоставлял бы взаимодействие программы с API на C. Поэтому работать с этими API несколько сложнее, чем с обычными. Основной момент при изучении этих API заключается в том, что запросы, отправляемые к API связки ключей, обычно упакованы в словарях. Например, если требуется запросить у сервисов связки ключей безопасное хранение тех или иных данных, то вы помещаете этот запрос в словарь (а вместе с запросом — все данные, которые собираетесь хранить, ключ к этим данным, идентификатор приложения и т. д.). Этот словарь вы отправляете к API, примером которого может служить функция SecItemAdd. Чтобы хранить информационный фрагмент в связке ключей, создайте словарь со следующими ключами:

• kSecClass — при необходимости хранения конфиденциальных информационных фрагментов, например строк, в качестве значения этого ключа обычно задается kSecClassGenericPassword;

• kSecAttrService — значение ключа чаще всего представляет собой строку. Как правило, эта строка — идентификатор нашего приложения;

• kSecAttrAccount — значением является строка, указывающая ключ к значению, которое мы хотим сохранить. Это произвольная строка, которая должна иметь смысл для вас и в контексте приложения;

• kSecValueData — значением является экземпляр NSData, который вы хотите сохранить по указанному ключу (kSecAttrAccount).

Возвращаемое значение функции SecItemAdd относится к типу OSStatus. Различные значения, которые вы можете получить от этой функции, определяются в файле SecBase.h SDK. Поэтому, находясь в Xcode, просто нажмите комбинацию клавиш Command+Shift+O, введите SecBase.h и попробуйте найти значение errSecSuccess. После того как найдете errSecSuccess в перечне, вы сможете просмотреть остальные значения, которые могут быть возвращены в экземпляре OSStatus:

enum

{

errSecSuccess = 0,

errSecUnimplemented = -4,

errSecParam = -50,

errSecAllocate = -108,

errSecNotAvailable = -25291,

errSecDuplicateItem = -25299,

errSecItemNotFound = -25300,

errSecInteractionNotAllowed = -25308,

errSecDecode = -26275,

errSecAuthFailed = -25293,

};

Если функция SecItemAdd завершится успешно, то в качестве ее возвращаемого значения вы получите errSecSuccess. Иное значение, получаемое от этой функции, означает ошибку. Итак, давайте объединим изученное и сделаем небольшой код, который будет записывать строковое значение в связку ключей:

#import «AppDelegate.h»

#import <Security/Security.h>

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *key = @"Full Name";

NSString *value = @"Steve Jobs";

NSData *valueData = [value dataUsingEncoding: NSUTF8StringEncoding];

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

NSDictionary *secItem = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccount: key,

(__bridge id)kSecValueData: valueData,

};

CFTypeRef result = NULL;

OSStatus status = SecItemAdd((__bridge CFDictionaryRef)secItem, &result);

if (status == errSecSuccess){

NSLog(@"Successfully stored the value");

} else {

NSLog(@"Failed to store the value with code: %ld", (long)status);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

При первом запуске этого приложения (предполагается, что вы выполнили все рекомендации из предыдущих разделов этой главы и правильно настроили свой профиль) вы получите от функции SecItemAdd значение errSecSuccess. Однако если вы повторно запустите это приложение, то получите значение errSecDuplicateItem. Таким образом iOS сообщает вам, что нельзя перезаписать имеющееся значение. В контексте строгих требований безопасности, применяемых в связке ключей, перезаписать имеющееся значение действительно нельзя. Но вот обновить имеющееся значение вы можете. О том, как это делается, рассказано далее в данной главе.

См. также

Раздел 8.1.

8.3. Нахождение значений в связке ключей

Постановка задачи

Требуется найти в связке ключей имеющийся там элемент.

Решение

Воспользуйтесь функцией SecItemCopyMatching. Выполните следующие шаги.

1. Создайте словарь для передачи вышеупомянутой функции. Добавьте к словарю ключ kSecClass. Присвойте ключу такое значение, чтобы он отражал тип искомого элемента. Как правило, здесь требуется значение kSecClassGenericPassword.

2. Добавьте в словарь ключ kSecAttrService. В качестве значения этого ключа установите сервисную строку того элемента, который вы ищете. В этой главе в качестве имен сервисов мы используем идентификатор пакета нашего приложения и устанавливаем в качестве идентификаторов пакета всех приложений одну и ту же строку. Таким образом, одно приложение может считывать связку ключей, другое — записывать в нее данные и т. д.

3. Добавьте в словарь ключ kSecAttrAccount и задайте в качестве его значения текущий ключ того значения, которое вы ранее сохранили в связке ключей. Если вы внимательно проработали пример из раздела 8.2, то в данном случае именем учетной записи будет служить строка Full Name.

4. Добавьте к словарю атрибут kSecReturnAttributes. В качестве его значения задайте kCFBooleanTrue, если хотите получить атрибуты значения, присутствующего в связке ключей (например, дату создания и дату последнего изменения). Если хотите получить текущее значение элемента, сохраненного вами в связке ключей, а не ключ kSecReturnAttributes, добавьте к словарю ключ kSecReturnData и задайте для него значение kCFBooleanTrue.

Как только ваш словарь будет готов, вы сможете передать его функции SecItemCopyMatching в качестве первого параметра. Второй параметр — это указатель на тот объект, который будет возвращаться функцией. Этот указатель должен относиться к типу CFTypeRef *. Это обобщенный тип данных, и в каждом конкретном случае тип зависит от того, что именно вы передадите функции SecItemCopyMatching в качестве первого параметра. Например, если в вашем словаре содержится ключ kSecReturnAttributes, то вторым параметром этой функции должен быть либо NULL, либо указатель на непрозрачный тип CFDictionaryRef. Если же вместо этого вы передадите словарю ключ kSecReturnData, то второй параметр этой функции должен относиться к типу CFDataRef. Это непрозрачный тип, который будет получать точные данные имеющегося элемента. Затем вы сможете преобразовать эти данные в экземпляр NSString и работать с ним.

Обсуждение

Предположим, вы хотите считать свойства той строки, которую записали в связку ключей в разделе 8.2. Можно написать такой код:

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *keyToSearchFor = @"Full Name";

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

NSDictionary *query = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccount: keyToSearchFor,

(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,

};

CFDictionaryRef valueAttributes = NULL;

OSStatus results = SecItemCopyMatching((__bridge CFDictionaryRef)query,

(CFTypeRef *)&valueAttributes);

NSDictionary *attributes =

(__bridge_transfer NSDictionary *)valueAttributes;

if (results == errSecSuccess){

NSString *key, *accessGroup, *creationDate, *modifiedDate, *service;

key = attributes[(__bridge id)kSecAttrAccount];

accessGroup = attributes[(__bridge id)kSecAttrAccessGroup];

creationDate = attributes[(__bridge id)kSecAttrCreationDate];

modifiedDate = attributes[(__bridge id)kSecAttrModificationDate];

service = attributes[(__bridge id)kSecAttrService];

NSLog(@"Key = %@\n \

Access Group = %@\n \

Creation Date = %@\n \

Modification Date = %@\n \

Service = %@", key, accessGroup, creationDate,

modifiedDate, service);

} else {

NSLog(@"Error happened with code: %ld", (long)results);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

После запуска этого приложения на консоль будут выведены примерно такие результаты:

Key = Full Name

Access Group = F3FU372W5M.com.pixolity.ios.cookbook.SecurityApp

Creation Date = 2013-06-09 10:44:55 +0000

Modification Date = 2013-06-09 10:44:55 +0000

Service = com.pixolity.ios.cookbook.SecurityApp

Это, конечно, хорошо, но как считывать актуальную информацию о значении? В подразделе «Решение» данного раздела я уже ответил на этот вопрос: необходимо включить в запрос ключ kSecReturnData. После того как это будет сделано, в качестве второго параметра функция SecItemCopyMatching потребует либо NULL, либо непрозрачную переменную CFDataRef, вот так:

#import «AppDelegate.h»

#import <Security/Security.h>

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *keyToSearchFor = @"Full Name";

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

NSDictionary *query = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccount: keyToSearchFor,

(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,

};

CFDataRef cfValue = NULL;

OSStatus results = SecItemCopyMatching((__bridge CFDictionaryRef)query,

(CFTypeRef *)&cfValue);

if (results == errSecSuccess){

NSString *value = [[NSString alloc]

initWithData:(__bridge_transfer NSData *)cfValue

encoding: NSUTF8StringEncoding];

NSLog(@"Value = %@", value);

} else {

NSLog(@"Error happened with code: %ld", (long)results);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

По умолчанию функция SecItemCopyMatching ищет первое совпадение в связке ключей. Допустим, вы сохранили в связке ключей 10 безопасных элементов класса kSecClassGenericPassword и хотите запросить их все. Как это сделать? Ничего сложного. Просто добавьте ключ kSecMatchLimit в словарь вашего запроса и укажите максимальное количество совпадающих элементов, которые сервисы должны отыскивать в связке ключей. Можно также присвоить этому ключу значение kSecMatchLimitAll — при нем осуществляется поиск всех совпадений. Когда вы внедрите ключ kSecMatchLimit в ваш словарь запроса для функции SecItemCopyMatching, второй параметр этого типа обязательно потребует указатель на непрозрачный тип CFArrayRef, а состоять этот массив будет только из тех элементов, которые вы запросили.

Рассмотрим пример, в котором мы пытаемся найти все элементы связки ключей, удовлетворяющие определенным критериям:

#import «AppDelegate.h»

#import <Security/Security.h>

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *keyToSearchFor = @"Full Name";

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

NSDictionary *query = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccount: keyToSearchFor,

(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,

(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll

};

CFArrayRef allCfMatches = NULL;

OSStatus results = SecItemCopyMatching((__bridge CFDictionaryRef)query,

(CFTypeRef *)&allCfMatches);

if (results == errSecSuccess){

NSArray *allMatches = (__bridge_transfer NSArray *)allCfMatches;

for (NSData *itemData in allMatches){

NSString *value = [[NSString alloc]

initWithData: itemData

encoding: NSUTF8StringEncoding];

NSLog(@"Value = %@", value);

}

} else {

NSLog(@"Error happened with code: %ld", (long)results);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

См. также

Раздел 8.2.

8.4. Обновление значений в связке ключей

Постановка задачи

Вы уже сохранили значение в связке ключей, а теперь хотите обновить его.

Решение

Учитывая, что вы смогли найти значение в связке ключей (см. раздел 8.3), можно схожим образом выполнить функцию SecItemUpdate с двумя параметрами. Ее первым параметром будет словарь вашего запроса, а вторым — словарь, описывающий изменения, которые вы хотите внести в имеющееся значение. Обычно этот обновляющий словарь (второй параметр метода) содержит всего один ключ (kSecValueData). Значением этого словарного ключа являются данные, которые нужно заново установить для имеющегося в связке ключа.

Обсуждение

Допустим, вы выполнили все указания, изложенные в разделе 8.2, и сохранили в связке ключей приложения строку Steve Jobs с ключом Full Name. Теперь вы хотите обновить это значение. Первым делом понадобится определить, присутствует ли уже нужное значение в связке ключей. Для этого создадим простой запрос, который вы уже видели ранее в этой главе:

NSString *keyToSearchFor = @"Full Name";

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

Страницы: «« ... 3536373839404142 ... »»

Читать бесплатно другие книги:

Рассмотрены основы информатики и описаны современные аппаратные средства персонального компьютера. С...
Молодые парни из экстремистской организации «Русский трибунал» объявили партизанскую войну «предател...
Покончив с несчастливым браком, Грейс решила, что больше никогда не доверится мужчине и не поставит ...
Лиз Сазерленд только мечтала о том, чтобы черная полоса ее жизни когда-нибудь сменилась белой. Пробл...
В старину ставили храмы на полях сражений в память о героях и мучениках, отдавших за Родину жизнь. Н...
В учебном пособии представлены вариативные авторские методики воспитания и развития волевых качеств ...