Подписание и проверка
Подписание и проверка подобны шифрованию, но несколько более сложны вследствие природы форматов с открытым ключом.
Как описано далее в Обзоре безопасности, шифрование с открытым ключом разработано для безопасной связи с потенциально недоверяемой стороной. Это использует два ключа — открытый ключ и закрытый ключ — которые математически связаны. Вы предоставляете свой открытый ключ недоверяемой стороне. Если та сторона использует открытый ключ для шифрования данных, можно позже дешифровать его с помощью закрытого ключа, и наоборот.
Подписание используется, чтобы доказать, что сообщение не было изменено в пути. Вы подписываете сообщение первым взятием хеша того сообщения, затем с помощью закрытого ключа для шифрования того хеша. Получатель может позже вычислить его или ее собственный хеш, использовать Ваш открытый ключ, чтобы дешифровать исходный хеш и сравнить два значения. Если они соответствуют, получателя можно обоснованно уверить, что сообщение не было изменено (предполагающий, что схема хеширования достаточно устойчива). Этот процесс сравнения и дешифрования упоминается как заверение подписи.
Как с основным шифрованием и дешифрованием, подписание и проверка имеют две части: получение SecKeyRef объекты для ключа или ключей и выполнения подписания или проверки преобразовывают себя.
Получение объекта SecKeyRef для шифрования с открытым ключом
Извлечение ключей от цепочки для ключей
При использовании существующих открытых и закрытых ключей от цепочки для ключей считайте Сертификат, Ключ и Руководство по программированию Trust Services, чтобы изучить, как получить a SecKeychainItemRef объект для того ключа.
Как только Вы получили a SecKeychainItemRef, можно бросить его к a SecKeyRef для использования с этим API.
Импорт существующих открытых и закрытых ключей
Импорт и экспорт пар с закрытым ключом и с открытым ключом несколько более сложны, чем генерация новых ключей из-за числа различных широко использующихся форматов ключа.
Этот пример описывает, как импортировать и экспортировать пару ключей в PEM (Почта с усовершенствованной защитой) формат.
Прежде чем можно будет импортировать ключи, необходимо считать ключевые данные в a CFDataRef объект. Если Ваши ключевые данные находятся в файле, самый простой способ считать ключ с чтением, преобразовывают, как описано в Чтении Файлов.
Как только у Вас есть свои ключевые данные в a CFDataRef объект, можно создать ключ и объекты идентификационных данных на основе тех данных.
Создайте и заполните объект параметров с основным набором значений.
SecItemImportExportKeyParameters params;
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0; // See SecKeyImportExportFlags for details.
params.passphrase = NULL;
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
/* These two values are for import. */
params.keyUsage = NULL;
params.keyAttributes = NULL;
Добавьте пароль при желании.
Можно добавить пароль двумя способами:
Скажите OS X предлагать пользователю пароль путем установки
kSecKeySecurePassphraseфлаг в поле флагов. Можно настроить это диалоговое окно путем указания надлежащий (CFStringRef) значения дляalertTitleиalertPrompt.Это - рекомендуемый способ поддерживать вводимые пользователями пароли, потому что пароль никогда не сохранен в адресном пространстве Вашего приложения.
Получите пароль сами (от цепочки для ключей или некоторого другого источника), затем обеспечьте пароль в a
CFStringRefилиCFDataRefобъект путем присвоения егоparams.passphraseполе.
При желании укажите первоначальный список управления доступом для ключа с помощью
accessRefполе.Для приобретения знаний о списках управления доступом для ключей считайте документацию для
SecAccessCreateи связанные функции.
Отсюда на, процесс немного отличается в зависимости от того, экспортируете ли Вы ключ или импортируете открытый ключ (сохраненный как автономный ключ) или закрытый ключ (традиционно сохраненный как пара «открытый/закрытый ключ» в единственном контейнере).
Создайте и заполните ключевой массив использования.
CFMutableArrayRef keyUsage = CFArrayCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks
);
/* This example sets a lot of usage values.
Choose usage values that are appropriate
to your specific task. Possible values begin
with kSecAttrCan, and are defined in
SecItem.h */
CFArrayAppendValue(keyUsage, kSecAttrCanEncrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDecrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDerive);
CFArrayAppendValue(keyUsage, kSecAttrCanSign);
CFArrayAppendValue(keyUsage, kSecAttrCanVerify);
CFArrayAppendValue(keyUsage, kSecAttrCanWrap);
CFArrayAppendValue(keyUsage, kSecAttrCanUnwrap);
Создайте и заполните ключевой массив атрибутов.
CFMutableArrayRef keyAttributes = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks
);
В этом случае словарь атрибутов пуст. Однако любые ключевые константы атрибута кроме тех, которые начинают
kSecAttrCanпотенциально законны (или по крайней мере любые константы, которые целесообразны для определенного ключевого типа).Установите ключевое использование, и приписывает поля в объекте параметров.
params.keyUsage = keyUsage;
params.keyAttributes = keyAttributes;
Установите внешний формат и флаговые значения соответственно.
SecExternalFormat externalFormat = kSecFormatPEMSequence;
int flags = 0;
В целом, значение
flagsдолжен быть нуль. Если указанный формат требует брони PEM, он автоматически предоставлен.Экспортируйте ключ.
OSStatus oserr = SecItemExport(publickey,
externalFormat, // See SecExternalFormat for details
flags, // See SecItemImportExportFlags for details
¶ms,
(CFDataRef *)&pkdata);
if (oserr) {fprintf(stderr, "SecItemExport failed (oserr=%d)\n", oserr);
exit(-1);
}
Установите ключевое использование, и приписывает
NULL.params.keyUsage = NULL;
params.keyAttributes = NULL;
Установите тип изделия, внешний формат и флаговые значения соответственно.
SecExternalItemType itemType = kSecItemTypeCertificate;
SecExternalFormat externalFormat = kSecFormatPEMSequence;
int flags = 0;
Тип изделия должен обычно быть
kSecItemTypeCertificateпотому что идентификационные данные обычно сериализируются в форме, содержащей и закрытые и открытые ключи.Значение
flagsдолжен обычно быть нуль. Если броня PEM присутствует, она является автоматически неизолированной.Импортируйте закрытый ключ.
oserr = SecItemImport(cfdataprivatekey,
NULL, // filename or extension
&externalFormat, // See SecExternalFormat for details
&itemType, // item type
flags, // See SecItemImportExportFlags for details
¶ms,
NULL, // Don't import into a keychain
&temparray);
if (oserr) {fprintf(stderr, "SecItemImport failed (oserr=%d)\n", oserr);
CFShow(temparray);
exit(-1);
}
privatekey = (SecKeyRef)CFArrayGetValueAtIndex(temparray, 0);
Установите ключевое использование, и приписывает
NULL.params.keyUsage = NULL;
params.keyAttributes = NULL;
Установите тип изделия, внешний формат и флаговые значения соответственно.
SecExternalItemType itemType = kSecItemTypePublicKey;
SecExternalFormat externalFormat = kSecFormatPEMSequence;
int flags = 0;
В целом, значение
flagsдолжен быть нуль. Если броня PEM присутствует, она является автоматически неизолированной.Импортируйте открытый ключ.
oserr = SecItemImport(cfdatapublickey,
NULL, // filename or extension
&externalFormat, // See SecExternalFormat for details
&itemType, // item type
flags, // See SecItemImportExportFlags for details
¶ms,
NULL, // Don't import into a keychain
&temparray);
if (oserr) {fprintf(stderr, "SecItemImport failed (oserr=%d)\n", oserr);
CFShow(temparray);
exit(-1);
}
publickey = (SecKeyRef)CFArrayGetValueAtIndex(temparray, 0);
Генерация Новой Пары «открытый/закрытый ключ»
Как правило, пары «открытый/закрытый ключ» сгенерированы вне объема Вашего приложения — или вручную использование Ассистента Сертификата или усовершенствованными конечными пользователями или системными администраторами, работающими openssl команды. В таких ситуациях или Вы или пользователь или администратор тогда отправляете сертификат, подписывая запрос к центру сертификации, подписывающему открытый ключ.
В некоторых случаях, однако, Ваше приложение, возможно, должно было бы генерировать пары «открытый/закрытый ключ» от имени пользователя. При этих обстоятельствах можно принять решение запустить Ассистент Сертификата, или можно принять решение генерировать пару ключей сами и обработать все в к приложению.
Несмотря на то, что существует много способов генерировать пару ключей для шифрования с открытым ключом, предпочтительный путь с SecKeyGeneratePair функция.
Создайте словарь параметров.
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
Заполните словарь параметров.
Например, для создания 4096-разрядного ключа RSA Вы использовали бы код как следующее:
CFDictionarySetValue(parameters,
kSecAttrKeyType,
kSecAttrKeyTypeRSA);
int rawnum = 4096;
CFNumberRef num = CFNumberCreate(
kCFAllocatorDefault,
kCFNumberIntType, &rawnum);
CFDictionarySetValue(
parameters,
kSecAttrKeySizeInBits,
num);
Необходимо указать (по крайней мере), размер ключа и ключевой тип. Для списка других дополнительных параметров см. документацию для
SecKeyGeneratePairв сертификате, ключе и доверительной ссылке служб.Генерируйте пару ключей.
SecKeyGeneratePair(parameters, &publickey, &privatekey);
Подписание и проверка
Как только Вы получили или закрытый ключ с открытым ключом в форме a SecKeyRef (или SecKeychainItemRef) объект, Вы готовы заверить данные знака или подписи. Сам процесс подписания является относительно прямым.
Создайте объект преобразования.
/* Create the transform objects */
signer = SecSignTransformCreate(privatekey, &error);
if (error) { CFShow(error); exit(-1); }Укажите
CFDataRefвозразите для использования в качестве источника данных.SecTransformSetAttribute(
signer,
kSecTransformInputAttributeName,
sourceData,
&error);
if (error) { CFShow(error); exit(-1); }Выполните преобразование.
signature = SecTransformExecute(signer, &error);
if (error) { CFShow(error); exit(-1); }if (!signature) {fprintf(stderr, "Signature is NULL!\n");
exit(-1);
}
Создайте объект преобразования.
verifier = SecVerifyTransformCreate(publickey, signature, &error);
if (error) { CFShow(error); exit(-1); }Заметьте, что ранее полученная подпись предоставлена при создании преобразования.
Укажите
CFDataRefвозразите для использования в качестве источника данных.SecTransformSetAttribute(
verifier,
kSecTransformInputAttributeName,
sourceData,
&error);
if (error) {CFShow(error);
exit(-1);
}
Выполните преобразование.
result = SecTransformExecute(verifier, &error);
if (error) {CFShow(error);
exit(-1);
}
Проверьте результат.
if (result == kCFBooleanTrue) {/* Signature was valid. */
} else {/* Signature was invalid. */
}