Подписание и проверка
Подписание и проверка подобны шифрованию, но несколько более сложны вследствие природы форматов с открытым ключом.
Как описано далее в Обзоре безопасности, шифрование с открытым ключом разработано для безопасной связи с потенциально недоверяемой стороной. Это использует два ключа — открытый ключ и закрытый ключ — которые математически связаны. Вы предоставляете свой открытый ключ недоверяемой стороне. Если та сторона использует открытый ключ для шифрования данных, можно позже дешифровать его с помощью закрытого ключа, и наоборот.
Подписание используется, чтобы доказать, что сообщение не было изменено в пути. Вы подписываете сообщение первым взятием хеша того сообщения, затем с помощью закрытого ключа для шифрования того хеша. Получатель может позже вычислить его или ее собственный хеш, использовать Ваш открытый ключ, чтобы дешифровать исходный хеш и сравнить два значения. Если они соответствуют, получателя можно обоснованно уверить, что сообщение не было изменено (предполагающий, что схема хеширования достаточно устойчива). Этот процесс сравнения и дешифрования упоминается как заверение подписи.
Как с основным шифрованием и дешифрованием, подписание и проверка имеют две части: получение 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. */
}