Подписание и проверка

Подписание и проверка подобны шифрованию, но несколько более сложны вследствие природы форматов с открытым ключом.

Как описано далее в Обзоре безопасности, шифрование с открытым ключом разработано для безопасной связи с потенциально недоверяемой стороной. Это использует два ключа — открытый ключ и закрытый ключ — которые математически связаны. Вы предоставляете свой открытый ключ недоверяемой стороне. Если та сторона использует открытый ключ для шифрования данных, можно позже дешифровать его с помощью закрытого ключа, и наоборот.

Подписание используется, чтобы доказать, что сообщение не было изменено в пути. Вы подписываете сообщение первым взятием хеша того сообщения, затем с помощью закрытого ключа для шифрования того хеша. Получатель может позже вычислить его или ее собственный хеш, использовать Ваш открытый ключ, чтобы дешифровать исходный хеш и сравнить два значения. Если они соответствуют, получателя можно обоснованно уверить, что сообщение не было изменено (предполагающий, что схема хеширования достаточно устойчива). Этот процесс сравнения и дешифрования упоминается как заверение подписи.

Как с основным шифрованием и дешифрованием, подписание и проверка имеют две части: получение SecKeyRef объекты для ключа или ключей и выполнения подписания или проверки преобразовывают себя.

Получение объекта SecKeyRef для шифрования с открытым ключом

Извлечение ключей от цепочки для ключей

При использовании существующих открытых и закрытых ключей от цепочки для ключей считайте Сертификат, Ключ и Руководство по программированию Trust Services, чтобы изучить, как получить a SecKeychainItemRef объект для того ключа.

Как только Вы получили a SecKeychainItemRef, можно бросить его к a SecKeyRef для использования с этим API.

Импорт существующих открытых и закрытых ключей

Импорт и экспорт пар с закрытым ключом и с открытым ключом несколько более сложны, чем генерация новых ключей из-за числа различных широко использующихся форматов ключа.

Этот пример описывает, как импортировать и экспортировать пару ключей в PEM (Почта с усовершенствованной защитой) формат.

Прежде чем можно будет импортировать ключи, необходимо считать ключевые данные в a CFDataRef объект. Если Ваши ключевые данные находятся в файле, самый простой способ считать ключ с чтением, преобразовывают, как описано в Чтении Файлов.

Как только у Вас есть свои ключевые данные в a CFDataRef объект, можно создать ключ и объекты идентификационных данных на основе тех данных.

bullet
Создать объекты параметра для импорта и экспорта
  1. Создайте и заполните объект параметров с основным набором значений.

        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;
  2. Добавьте пароль при желании.

    Можно добавить пароль двумя способами:

    • Скажите OS X предлагать пользователю пароль путем установки kSecKeySecurePassphrase флаг в поле флагов. Можно настроить это диалоговое окно путем указания надлежащий (CFStringRef) значения для alertTitle и alertPrompt.

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

    • Получите пароль сами (от цепочки для ключей или некоторого другого источника), затем обеспечьте пароль в a CFStringRef или CFDataRef объект путем присвоения его params.passphrase поле.

  3. При желании укажите первоначальный список управления доступом для ключа с помощью accessRef поле.

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

Отсюда на, процесс немного отличается в зависимости от того, экспортируете ли Вы ключ или импортируете открытый ключ (сохраненный как автономный ключ) или закрытый ключ (традиционно сохраненный как пара «открытый/закрытый ключ» в единственном контейнере).

bullet
Экспортировать ключи в объект CFDataRef
  1. Создайте и заполните ключевой массив использования.

        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);
  2. Создайте и заполните ключевой массив атрибутов.

        CFMutableArrayRef keyAttributes = CFArrayCreateMutable(
            kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks
        );

    В этом случае словарь атрибутов пуст. Однако любые ключевые константы атрибута кроме тех, которые начинают kSecAttrCan потенциально законны (или по крайней мере любые константы, которые целесообразны для определенного ключевого типа).

  3. Установите ключевое использование, и приписывает поля в объекте параметров.

        params.keyUsage = keyUsage;
        params.keyAttributes = keyAttributes;
  4. Установите внешний формат и флаговые значения соответственно.

        SecExternalFormat externalFormat = kSecFormatPEMSequence;
        int flags = 0;

    В целом, значение flags должен быть нуль. Если указанный формат требует брони PEM, он автоматически предоставлен.

  5. Экспортируйте ключ.

        OSStatus oserr = SecItemExport(publickey,
            externalFormat, // See SecExternalFormat for details
            flags, // See SecItemImportExportFlags for details
            &params,
            (CFDataRef *)&pkdata);
        if (oserr) {
            fprintf(stderr, "SecItemExport failed (oserr=%d)\n", oserr);
            exit(-1);
        }
bullet
Импортировать идентификационные данные (содержащий пару «открытый/закрытый ключ») от объекта CFDataRef
  1. Установите ключевое использование, и приписывает NULL.

        params.keyUsage = NULL;
        params.keyAttributes = NULL;
  2. Установите тип изделия, внешний формат и флаговые значения соответственно.

        SecExternalItemType itemType = kSecItemTypeCertificate;
        SecExternalFormat externalFormat = kSecFormatPEMSequence;
        int flags = 0;

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

    Значение flags должен обычно быть нуль. Если броня PEM присутствует, она является автоматически неизолированной.

  3. Импортируйте закрытый ключ.

        oserr = SecItemImport(cfdataprivatekey,
            NULL, // filename or extension
            &externalFormat, // See SecExternalFormat for details
            &itemType, // item type
            flags, // See SecItemImportExportFlags for details
            &params,
            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);
bullet
Импортировать сертификат открытых ключей из объекта CFDataRef
  1. Установите ключевое использование, и приписывает NULL.

        params.keyUsage = NULL;
        params.keyAttributes = NULL;
  2. Установите тип изделия, внешний формат и флаговые значения соответственно.

        SecExternalItemType itemType = kSecItemTypePublicKey;
        SecExternalFormat externalFormat = kSecFormatPEMSequence;
        int flags = 0;

    В целом, значение flags должен быть нуль. Если броня PEM присутствует, она является автоматически неизолированной.

  3. Импортируйте открытый ключ.

        oserr = SecItemImport(cfdatapublickey,
            NULL, // filename or extension
            &externalFormat, // See SecExternalFormat for details
            &itemType, // item type
            flags, // See SecItemImportExportFlags for details
            &params,
            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 функция.

bullet
Генерировать пару «открытый/закрытый ключ»
  1. Создайте словарь параметров.

        CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(
            kCFAllocatorDefault,
            0,
            &kCFTypeDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);
     
  2. Заполните словарь параметров.

    Например, для создания 4096-разрядного ключа RSA Вы использовали бы код как следующее:

        CFDictionarySetValue(parameters,
            kSecAttrKeyType,
            kSecAttrKeyTypeRSA);
     
        int rawnum = 4096;
        CFNumberRef num = CFNumberCreate(
            kCFAllocatorDefault,
            kCFNumberIntType, &rawnum);
     
        CFDictionarySetValue(
            parameters,
            kSecAttrKeySizeInBits,
            num);
     

    Необходимо указать (по крайней мере), размер ключа и ключевой тип. Для списка других дополнительных параметров см. документацию для SecKeyGeneratePair в сертификате, ключе и доверительной ссылке служб.

  3. Генерируйте пару ключей.

        SecKeyGeneratePair(parameters, &publickey, &privatekey);

Подписание и проверка

Как только Вы получили или закрытый ключ с открытым ключом в форме a SecKeyRef (или SecKeychainItemRef) объект, Вы готовы заверить данные знака или подписи. Сам процесс подписания является относительно прямым.

bullet
Подписать содержание объекта CFDataRef
  1. Создайте объект преобразования.

        /* Create the transform objects */
        signer = SecSignTransformCreate(privatekey, &error);
        if (error) { CFShow(error); exit(-1); }
     
  2. Укажите CFDataRef возразите для использования в качестве источника данных.

        SecTransformSetAttribute(
                                 signer,
                                 kSecTransformInputAttributeName,
                                 sourceData,
                                 &error);
        if (error) { CFShow(error); exit(-1); }
  3. Выполните преобразование.

        signature = SecTransformExecute(signer, &error);
        if (error) { CFShow(error); exit(-1); }
     
        if (!signature) {
            fprintf(stderr, "Signature is NULL!\n");
            exit(-1);
        }
bullet
Заверять подпись
  1. Создайте объект преобразования.

        verifier = SecVerifyTransformCreate(publickey, signature, &error);
        if (error) { CFShow(error); exit(-1); }

    Заметьте, что ранее полученная подпись предоставлена при создании преобразования.

  2. Укажите CFDataRef возразите для использования в качестве источника данных.

        SecTransformSetAttribute(
                                 verifier,
                                 kSecTransformInputAttributeName,
                                 sourceData,
                                 &error);
        if (error) {
            CFShow(error);
            exit(-1);
        }
  3. Выполните преобразование.

        result = SecTransformExecute(verifier, &error);
        if (error) {
            CFShow(error);
            exit(-1);
        }
  4. Проверьте результат.

        if (result == kCFBooleanTrue) {
            /* Signature was valid. */
        } else {
            /* Signature was invalid. */
        }