Сертификат, ключ и доверительные задачи служб для OS X

В этой главе описываются и иллюстрирует использование Сертификата, Ключа, и функций Trust Services, чтобы оценить доверие сертификата, определить причину доверительного отказа и восстановиться с доверительного отказа.

Последовательность операций, проиллюстрированных в этой главе:

  1. Найдите сертификат в цепочке для ключей.

  2. Получите объект политики для политики, используемой в оценке сертификата.

  3. Проверьте сертификат и оцените, можно ли ему доверять, как указано политикой.

  4. Тест для восстанавливаемой доверительной ошибки.

  5. Определите, является ли доверительная ошибка вследствие сертификата с истекшим сроком.

  6. Измените критерии оценки для игнорирования истекших сертификатов.

  7. Переоцените сертификат.

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

Нахождение сертификата на цепочке для ключей

Прежде чем можно будет оценить доверие сертификата, необходимо получить ссылочный объект для сертификата. Можно получить объект сертификата при помощи Безопасного Транспортного API SSLGetPeerCertificates функция, путем создания один от данных сертификата с помощью SecCertificateCreateFromData функция, или путем нахождения сертификата на цепочке для ключей.

Перечисление 3-1 показывает пример кода для получения объекта сертификата путем нахождения сертификата на цепочке для ключей. В этой выборке сертификат идентифицируется адресом электронной почты владельца сертификата. Можно использовать другие атрибуты сертификата с этой целью, такие как метка элемента цепочки для ключей или его даты модификации. Подробное объяснение каждой пронумерованной строки кода следует за перечислением.

Перечисление 3-1  , Находящее сертификат на цепочке для ключей

#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <CoreServices/CoreServices.h>
OSStatus GetCertRef (SecKeychainAttributeList *attrList,
                                    SecKeychainItemRef *itemRef)
{
    OSStatus status;
    SecKeychainSearchRef searchReference = nil;
 
    status = SecKeychainSearchCreateFromAttributes (    // 1
        NULL,                                           // 2
        kSecCertificateItemClass,                       // 3
        attrList,                                       // 4
        &searchReference
    );
 
    status = SecKeychainSearchCopyNext (                // 5
        searchReference,
        itemRef
        );
    if (searchReference)
        CFRelease(searchReference);                     // 6
 
    return (status);
}
int main (int argc, const char * argv[]) {
    OSStatus status;
    SecKeychainItemRef itemRef = nil;
    SecKeychainAttributeList attrList;
    SecKeychainAttribute attrib;
    attrList.count = 1;                                 // 7
    attrList.attr  = &attrib;
    attrib.tag = kSecAlias;                             // 8
    attrib.data = "emailname@domain.com";               // 9
    attrib.length = strlen(attrib.data);
 
    status = GetCertRef (&attrList, &itemRef);
    .
    .
    .
    if (itemRef)
        CFRelease(itemRef);                             // 10
    return (status);
    }

Вот то, что делает код:

  1. Устанавливает критерии поиска элемента цепочки для ключей и получает поисковый ссылочный объект. От этого объекта нужно избавиться, когда он больше не необходим.

  2. Передачи NULL для использования цепочки для ключей по умолчанию ищут список.

  3. Указывает, что поиск для сертификата.

  4. Обеспечивает список атрибутов для соответствия. Атрибуты определяются в основной подпрограмме (см. шаги 7 - 9).

  5. Находит сертификат на цепочке для ключей и получает ссылочный объект элемента цепочки для ключей. От этого объекта нужно избавиться, когда больше не необходимый. Обратите внимание на то, что объект элемента цепочки для ключей для сертификата может быть брошен к объекту сертификата.

  6. Избавляется от поискового ссылочного объекта, который больше не необходим.

  7. Указывает, что существует только один атрибут в списке атрибутов.

  8. Указывает, что атрибут должен иметь тип kSecAlias. В случае сертификата это указывает, что атрибут является адресом электронной почты владельца сертификата.

  9. Указывает адрес электронной почты для поиска.

  10. Избавляется от объекта элемента цепочки для ключей в конце подпрограммы, после того, как это использовалось для оценки доверия (Перечисление 3-3).

Получение объекта политики

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

Перечисление 3-2 показывает, как можно получить объект политики для использования в оценке. Для использования этой процедуры необходимо знать идентификатор объекта (OID) политики. OIDs политики, проводившей модулем AppleX509TP CDSA, показаны в Политиках Доверия AppleX509TP Сертификата, Ключа и Ссылки Trust Services. Подробное объяснение каждой пронумерованной строки кода следует за перечислением.

Перечисление 3-2  Получая ссылочный объект политики

OSStatus FindPolicy (const CSSM_OID *policyOID, SecPolicyRef *policyRef)
{
    OSStatus status1;
    OSStatus status2;
    SecPolicySearchRef searchRef;
 
    status1 = SecPolicySearchCreate (               // 1
        CSSM_CERT_X_509v3,                          // 2
        policyOID,                                  // 3
        NULL,
        &searchRef
        );
 
    status2 = SecPolicySearchCopyNext (             // 4
        searchRef,
        policyRef
        );
 
    if (searchRef)
        CFRelease(searchRef);                       // 5
    return (status2);
}
int main (int argc, const char * argv[]) {
    OSStatus status;
    const CSSM_OID *myPolicyOID = &CSSMOID_APPLE_X509_BASIC;
    SecPolicyRef policyRef = nil;
    status = FindPolicy (myPolicyOID, &policyRef);
    .
    .
    .
    if (policyRef)
        CFRelease(policyRef);                        // 6
    return (status);
    }

Вот то, что делает код:

  1. Устанавливает критерии поиска политики и получает ссылочный объект поиска политики. От этого объекта нужно избавиться, когда больше не необходимый.

  2. Указывает тип сертификатов, используемых политикой. Указать CSSM_CERT_X_509v3 если Вы не уверены из типа сертификата.

  3. Указывает OID политики.

  4. Находит политику и получает ссылочный объект политики. От этого объекта нужно избавиться, когда больше не необходимый.

  5. Избавляется от поискового ссылочного объекта, который больше не необходим.

  6. Избавляется от объекта политики в конце подпрограммы, после того, как это использовалось для оценки доверия (Перечисление 3-3).

Оценка доверия

Получив объект сертификата (Перечисление 3-1) и объект политики (Перечисление 3-2), можно оценить доверие сертификата. Если Вы знаете — или имеете хорошее предположение для — промежуточные и корневые сертификаты должны были проверить сертификат, можно включать их в массив сертификатов, переданных SecTrustCreateWithCertificates функция. Принимая во внимание, что промежуточные и корневые сертификаты могут быть в любом порядке, листовой сертификат — тот, который Вы хотите оценить — должен быть первым в массиве. Можно включать сертификаты, не необходимые для оценки без вредных воздействий. Любые сертификаты в цепочке сертификата, в которой Вы не передаете функции, разыскиваются в цепочках для ключей в системе. Сертификаты и цепочки сертификата обсуждены в Обзоре безопасности.

Перечисление 3-3 иллюстрирует, как Вы используете Сертификат, Ключ и Доверительные функции для оценки доверия сертификата. Подробное объяснение каждой пронумерованной строки кода следует за перечислением.

Перечисление 3-3  Оценивая доверие

OSStatus EvaluateCert (SecCertificateRef cert, CFTypeRef policyRef,
                            SecTrustResultType *result,
                            SecTrustRef *pTrustRef)
{
    OSStatus status1;
    OSStatus status2;
 
    SecCertificateRef evalCertArray[1] = { cert };  // 1
    CFArrayRef cfCertRef = CFArrayCreate ((CFAllocatorRef) NULL,
                                        (void *)evalCertArray, 1,
                                        &kCFTypeArrayCallBacks);
    if (!cfCertRef)
        return memFullErr;                          // 2
 
    status1 = SecTrustCreateWithCertificates (      // 3
        cfCertRef,                                  // 4
        policyRef,                                  // 5
        pTrustRef
        );
    if (status1)
        return status1;
 
    status2 = SecTrustEvaluate (                    // 6
        *pTrustRef,
        result                                      // 7
        );
    // Release the objects we allocated
    if (cfCertRef)
        CFRelease(cfCertRef);
    if (cfDate)
        CFRelease(cfDate);
 
    return (status2);
}
int main (int argc, const char * argv[]) {
    OSStatus status;
    OSStatus status1;
    OSStatus status2;
 
    SecKeychainItemRef itemRef = nil;
    SecKeychainAttributeList attrList;
    SecKeychainAttribute attrib;
    attrList.count = 1;
    attrList.attr  = &attrib;
    attrib.tag = kSecAlias;
    attrib.data = "emailname@domain.com";
    attrib.length = strlen(attrib.data);
 
    const CSSM_OID *myPolicyOID = &CSSMOID_APPLE_X509_BASIC;
    SecPolicyRef policyRef = nil;
 
    SecTrustRef trustRef = nil;
    SecTrustResultType result;
 
    CFArrayRef certChain;
    CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = nil;
 
    status = GetCertRef (&attrList, &itemRef);      // 8
    status1 = FindPolicy (myPolicyOID, &policyRef); // 9
 
    status2 = EvaluateCert (
                            (SecCertificateRef)itemRef,
                            (CFTypeRef) policyRef,
                             &result, &trustRef);   // 10
    .
    .
    .
    if (itemRef)
        CFRelease(itemRef);
    if (policyRef)
        CFRelease(policyRef);
    if (trustRef)
        CFRelease(trustRef);                        // 11
    return (status2);
    }

Вот то, что делает код:

  1. Делает CFArray одного элемента из предоставленного сертификата. См. Перечисление 3-1 для кода для нахождения сертификата на цепочке для ключей.

  2. Возвраты с ошибкой, если это не может выделить массив.

  3. Устанавливает доверительные критерии оценки и получает доверительный объект управления. От этого объекта нужно избавиться, когда больше не необходимый.

  4. Обеспечивает массив сертификатов, содержа сертификат, который будет оценен и возможно промежуточные и корневые сертификаты, которые могли бы быть необходимы в оценке. В этой выборке массив содержит только один сертификат.

  5. Указывает ссылочный объект политики политики к используемому в оценке этого сертификата. См. Перечисление 3-2 для кода для получения ссылочного объекта политики.

  6. Оценивает сертификат согласно указанной политике.

  7. Возвращает объект результата, использующийся для получения информации о результате оценки; см. Перечисление 3-5.

  8. Получает ссылочный объект сертификата; см. Перечисление 3-1.

  9. Получает ссылочный объект политики; см. Перечисление 3-2.

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

  11. Избавляется от доверительного ссылочного объекта, после того, как он использовался для восстановления с доверительного отказа (Перечисление 3-5).

Восстановление с доверительного отказа

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

Результат оценки kSecTrustResultRecoverableTrustFailure указывает, что доверие было отклонено, но что возможно изменить настройки для получения различного результата. Например, если сертификат, используемый для подписания документа, истек, можно изменить дату, используемую для оценки, чтобы видеть, был ли сертификат допустим, когда был подписан документ. Код в Перечислении 3-4 иллюстрирует, как изменить дату оценки. Обратите внимание на то, что CFDateCreate функция занимает абсолютное время (число секунд с 1 января 2001); можно использовать CFGregorianDateGetAbsoluteTime функционируйте для преобразования календарной даты и время в абсолютное время.

Перечисление 3-4  , Назначающее дату оценки

    OSStatus status;
 
    CFAbsoluteTime expDate;
    CFDateRef cfDate = nil;
    expDate = 157680000;  //seconds since 1 Jan 2001
    cfDate = CFDateCreate (NULL, expDate);
    status = SecTrustSetVerifyDate (*pTrustRef, cfDate);

Перечисление 3-5 иллюстрирует восстановление после доверительного отказа, вызванного сертификатом с истекшим сроком. В этом случае критерии оценки изменяются для игнорирования дат истечения срока. Подробное объяснение каждой пронумерованной строки кода следует за перечислением.

Перечисление 3-5  , Восстанавливающееся с доверительного отказа

     int n;
     CSSM_TP_APPLE_CERT_STATUS AllStatusBits = 0;
 
status3 = EvaluateCert (
                        (SecCertificateRef)itemRef,
                        (CFTypeRef) policyRef,
                        &result, &trustRef);                // 1
 
if (status3 == noErr)
    {
    if (result ==  kSecTrustResultRecoverableTrustFailure)  // 2
        {
            status3 = SecTrustGetResult (
                                        trustRef,           // 3
                                        &result,            // 4
                                        &certChain,         // 5
                                        &statusChain        // 6
                                        );
            if (!status3 && statusChain)
            {                                               // 7
                for (n = 0; n <                            // 8
                        CFArrayGetCount(certChain); n++)
                AllStatusBits =
                    AllStatusBits | statusChain[n].StatusBits;
            if (AllStatusBits & CSSM_CERT_STATUS_EXPIRED)
                {
                    CSSM_APPLE_TP_ACTION_DATA actionData;   // 9
                    actionData.Version =
                            CSSM_APPLE_TP_ACTION_VERSION;   // 10
                    actionData.ActionFlags =
                        CSSM_TP_ACTION_ALLOW_EXPIRED |
                        CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT;  // 11
 
                    CFDataRef myActionData =                // 12
                        CFDataCreateWithBytesNoCopy
                            (NULL, (UInt8*) &actionData,
                             sizeof(actionData),
                             kCFAllocatorNull);             // 13
 
                    if (myActionData)
                        {
                            status2 = SecTrustSetParameters (
                                        trustRef,
                                        CSSM_TP_ACTION_DEFAULT,
                                        myActionData);      // 14
                            status3 = SecTrustEvaluate (
                                        trustRef,
                                        &result
                                        );                  // 15
                            status3 = SecTrustGetResult (
                                        trustRef,
                                        &result,
                                        &certChain,
                                        &statusChain);      // 16
 
                            CFRelease(myActionData);
                    }
                }
                }
            }
    }

Вот то, что делает код:

  1. Оценивает доверие для определенного сертификата и политики (см. Перечисление 3-3).

  2. Проверяет результат оценки. Если был восстанавливаемый доверительный отказ, подпрограмма продолжает получать больше информации.

  3. Передает доверительный объект управления, полученный ранее.

  4. Передает указатель на объект результата, полученный ранее.

  5. Возвращается цепочка сертификата раньше проверяла оцененный сертификат.

  6. Возвращает массив структур, каждая из которых содержит информацию о состоянии одного сертификата в цепочке.

  7. Если функция успешно выполняется, проверки на законность statusChain. statusChain указатель оставляют неинициализированным если (result != kSecTrustResultRecoverableTrustFailure) или если (status3 != noErr).

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

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

  9. Выделяет площадь на штабеле для структуры данных действия.

  10. Заполняет Version поле структуры данных действия.

  11. Заполняет ActionFlags поле структуры данных действия для разрешения истекших сертификатов и корневых сертификатов.

  12. Создает CFDataRef из данных действия.

  13. Передачи kCFAllocatorNull как последний параметр (bytesDeallocator) так, чтобы байты, на которые указывает CFDataRef, не были освобождены автоматически.

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

  15. Переоценивает доверие.

  16. Проверяет результаты оценки.