【问题标题】:Boost SSL verifies expired and self-signed certificatesBoost SSL 验证过期和自签名证书
【发布时间】:2016-02-03 10:53:57
【问题描述】:

我正在使用 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/ 也是如此。肯定不对吧?

【问题讨论】:

  • 怪事。我会密切关注这篇文章,以防其他人在这里发现真正的原因。惊人的。每天都在学习新事物

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


【解决方案1】:

这里的问题是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 上下文,这可能无法工作。

【讨论】:

  • 我很高兴我学会了这一点。我认为它必须处于协议协商/握手阶段。但这对我来说是新的。非常有用的信息。
  • @sehe 我知道一些你不知道的事情是难得的一天。虽然我确实知道解决方案,但说实话,如果没有你的回答,我不会理解这个问题。
  • 啊,我明白了!是的,我注意到sslv3_client 不对。
【解决方案2】:

这很奇怪。

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

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

SHA1 Thumbprint = 079B3259D07C4DE2A1CE0EF4A5B5599D3B2D62EA

但是,当我尝试从我的 shell 中使用例如

 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

当我向您的代码 sn-p 添加回调时:

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 地址似乎解析为相同。但这似乎与症状有关。

【讨论】:

    猜你喜欢
    • 2011-12-03
    • 2011-10-19
    • 2017-04-18
    • 2012-11-01
    • 2014-01-26
    • 2018-06-18
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    相关资源
    最近更新 更多