2016-02-03 6 views
2

Я использую asio для Boost для подключения к сайту через HTTPS. Я хочу, чтобы это было успешным только в том случае, если сертификат действителен, не истек, а не сам по себе и т. Д. К сожалению, он всегда работает независимо. Вот мой код:Boost SSL проверяет истекшие и самоподписанные сертификаты

try 
{ 
    asio::io_service ioService; 
    asio::ssl::context sslContext(asio::ssl::context::sslv3_client); 

    sslContext.load_verify_file("cacert.pem"); 

    asio::ip::tcp::resolver resolver(ioService); 
    asio::ip::tcp::resolver::query query("self-signed.badssl.com", "443"); 
    asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query); 

    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(ioService, sslContext); 

    ioService.run(); 

    // Enable SSL peer verification. 
    socket.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert); 

    asio::connect(socket.lowest_layer(), endpointIterator); 

    socket.handshake(asio::ssl::stream_base::client); 

    boost::asio::streambuf request; 
    std::ostream requestStream(&request); 
    requestStream << "GET/HTTP/1.0\r\n"; 
    requestStream << "Host: self-signed.badssl.com\r\n"; 
    requestStream << "Accept: */*\r\n"; 
    requestStream << "Connection: close\r\n\r\n"; 

    asio::write(socket, request); 

И так далее. Если я использую set_verify_callback() и возвращаю false из обратного вызова, соединение не работает, но preverified всегда кажется правдой, даже для https://self-signed.badssl.com/. Неужели это неправильно?

+0

Weirdness. Я буду следить за этим сообщением на всякий случай, если кто-то видит здесь настоящую причину. Удивительно. Опираясь на что-то новое everyday – sehe

ответ

7

Вопрос здесь Server Name Indication (SNI):

Server Name Индикация (SNI) является расширением сетевой протокол TLS компьютера , с помощью которого клиент указывает, какой HostName он пытается подключиться к в начало процесса установления связи. Этот позволяет серверу предоставлять несколько сертификатов на одном и том же IP-адресе и номер порта TCP и, следовательно, позволяет нескольким защищенным (HTTPS) сайтам (или любой другой услуге через TLS) обслуживаться с одного и того же IP-адреса , не требуя при этом всех этих сайтов для использования того же сертификата.

Сервер badssl.com отправляет сертификат с надлежащей цепочкой при подключении без SNI. Если вы подключитесь к SNI, тогда будет отправлен самозаверяющий сертификат. Вы можете проверить это с помощью OpenSSL в командной строке, наблюдая разницу между этими двумя командами:

openssl s_client -connect self-signed.badssl.com:443 -showcerts 
openssl s_client -connect self-signed.badssl.com:443 -servername self-signed.badssl.com -showcerts 

boost::asio не имеет API для добавления SNI, но я думаю, что вы можете сделать это с помощью лежащей в основе OpenSSL API и native_handle() метод в вашем потоке.Это должно быть что-то вроде этого:

SSL_set_tlsext_host_name(socket.native_handle(), "self-signed.badssl.com"); 

Я не отметить, что вы настраиваете контекст с sslv3_client. Поскольку SNI является расширением TLS (т. Е. Не SSLv3), это может не работать без настройки контекста TLS.

+0

Я рад, что узнал об этом. Я подумал, что это должно быть в фазе переговоров/рукопожатия протокола. Но это было ново для меня. Очень полезная информация. – sehe

+0

@sehe Это редкий день, когда я знаю кое-что, чего у вас нет. И хотя я знал это решение, честно говоря, я бы не понял проблему без вашего ответа. – rhashimoto

+0

А я вижу! И да, я заметил, что 'sslv3_client' не прав. – Timmmm

2

Ну, это странно.

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

При проверке с помощью этого браузера или этого онлайн-инструмента: https://www.digicert.com/help/ это подтверждается. Он показывает, что сертификат:

SHA1 Thumbprint = 079B3259D07C4DE2A1CE0EF4A5B5599D3B2D62EA 

Однако, когда я попробовал из своей оболочки, используя, например,

openssl s_client -connect self-signed.badssl.com:443 -debug |& 
     openssl x509 -text -noout 

Мы получаем четко различимый сертификат с «реальным» эмитентом: Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO RSA Domain Validation Secure Server CA.

Мы ясно получить другой сертификат:

SHA1 Fingerprint=C8:67:8E:DB:FD:BB:30:B5:3F:2D:7B:F9:66:B8:14:C6:2E:95:92:CE 

Когда я добавил обратный вызов фрагмент кода:

auto cb = [](bool preverified, boost::asio::ssl::verify_context& ctx) { 
    char subject_name[256]; 
    X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); 
    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); 

    std::cout << "SSL Verify: " << subject_name << "\n"; 

    return preverified; 
}; 
socket.set_verify_callback(cb); 

asio::connect(socket.lowest_layer(), endpointIterator); 
std::cout << "Connected: " << socket.lowest_layer().remote_endpoint() << "\n"; 

system::error_code ec; 
socket.handshake(asio::ssl::stream_base::client, ec); 

std::cout << "Shook hands: " << ec.message() << "\n"; 

Я видел это действительно сертификат для меня ASIO регулировало:

Connected: 104.154.89.105:443 
SSL Verify: /C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root 
SSL Verify: /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority 
SSL Verify: /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA 
SSL Verify: /OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.badssl.com 
Shook hands: Success 

Я честно понятия не имею, как эти два могут не согласиться - даже когда появляется IP-адрес чтобы решить одно и то же. Но это, безусловно, имеет отношение к симптомам.

Смежные вопросы