Pull to refresh

Comments 6

Например, если OpenSSL вернул нам в этом колбеке ошибку - X509_V_ERR_CERT_HAS_EXPIRED, что значит, что сертификат уже "протух", но мы по своим причинам хотим разрешить такое непотребство, то мы можем на нее просто вернуть OK.

А если у сертификата не только эта проблема? Сертификат может быть не только протухим, но и иметь другие проблемы с валидностью.

Конечно, можно подавить любую ошибку. Это я написал как пример:) обновлю

Я немного про другое. Openssl выдает ошибку на первой же проблеме. Следующую проблему он не показывает. Если после этого вы включаете callback, который выдает ok, как вы узнаете, что больше проблем нет?

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

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

Контексты OpenSSL действительно удобны. Здесь нет на мой взгляд существенного уточнения о том, что контекст проверки сертификата одноразовый, то есть после того, как контекст был использован для проверки одного сертификата, его нельзя сразу же повторно переиспользовать для проверки другого сертификата. Нужно либо сбрасывать контекст функцией X509_STORE_CTX_reset(), либо создавать новый, то есть как-то так:

STACK_OF( X509 )* certsToVerify = ...; // Сертификаты, которые собираемся
                                       // проверить.
STACK_OF( X509 )* untrusted = ...;     // Промежуточные недоверенные сертификаты,
                                       // которые могут быть использованы для
                                       // построения цепочек.
X509_STORE* store = ...;               // Хранилище доверенных сертификатов,
                                       // уже сконфигурированное нужным образом.

X509_STORE_CTX* ctx = X509_STORE_CTX_new();
if( !ctx )
{
    HandleError();
}

for( int i = 0; i < sk_X509_num( certsToVerify ); ++i )
{
    X509* cert = sk_X509_value( certsToVerify, i );
    if( !X509_STORE_CTX_init( ctx, store, cert, untrusted ) )
    {
        HandleError();
    }

    if( 1 > X509_STORE_CTX_verify( ctx ) )
    {
        const char* err = X509_verify_cert_error_string( X509_STORE_CTX_get_error( ctx ) );
        X509_NAME* X509_get_subject_name( cert );
        fprintf( stderr, "Verification failure: %s\n", err );
        X509_NAME_print_ex( stderr, name, 0, 0 );
    }
    X509_STORE_CTX_reset( ctx ); // Сбрасываем контекст для проверки следующего сертификата.
}

А сбрасывать контекст перед освобождением, как это делаете Вы, смысла не имеет.

Да и обертки над X509_STORE_load_path(), X509_STORE_load_file(), X509_STORE_add_cert() избыточны, так как функции OpenSSL возвращают 1 при успехе и 0 при ошибке, что и так без проблем кастится в плюсовый bool, а так только дополнительный вызов получается.

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

Sign up to leave a comment.

Articles