Boost SSL验证过期和自签名证书

时间:2016-02-03 10:53:58

标签: ssl boost openssl ssl-certificate boost-asio

我使用Boost的asio通过HTTPS连接到网站。我希望这只有在证书有效,没有过期,没有自签名等情况下才能成功。不幸的是,似乎无论如何总是有效。这是我的代码:

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/也是如此。当然这不对吗?

2 个答案:

答案 0 :(得分:7)

这里的问题是Server Name Indication(SNI):

  

服务器名称指示(SNI)是TLS计算机的扩展   客户端指示它是哪个主机名的网络协议   尝试在握手过程开始时连接。这个   允许服务器在同一IP上显示多个证书   地址和TCP端口号因此允许多个安全(HTTPS)   网站(或任何其他TLS服务)将使用相同的IP提供服务   地址,不要求所有这些网站使用相同的证书。

当您连接没有SNI时,badssl.com服务器正在发送带有正确链的证书。如果您使用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没有添加SNI的API,但我认为您可以通过在流上使用基础OpenSSL API和native_handle()方法来实现。它应该是这样的:

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

我注意到您正在使用sslv3_client配置上下文。由于SNI是TLS扩展(即不是SSLv3),如果不配置TLS上下文,这可能无法正常工作。

答案 1 :(得分:2)

这很奇怪。

显然,服务器应该提供自签名证书。

使用浏览器或此在线工具查询时:https://www.digicert.com/help/甚至已确认。它显示证书是:

SHA1 Thumbprint = 079B3259D07C4DE2A1CE0EF4A5B5599D3B2D62EA

然而,当我尝试从我的shell使用例如

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

我们获得了一个明显不同的证书,其中包括&#34;真实&#34;发行人: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地址似乎已经解决了。但这似乎与症状有关。