与 Asio 和 OpenSSL 握手期间 *某些* 网站上的错误版本号错误

Wrong version number error on *some* websites during handshake with Asio and OpenSSL

我正在尝试使用 Asio 和 OpenSSL 发送 https 网络请求。我的代码在我试过的大多数网站上都运行良好,但在其他一些网站上,我在握手期间遇到错误 handshake: wrong version number (SSL routines, ssl3_get_record)

我发现有些人遇到这个问题是因为他们在代理后面,或者因为他们试图连接到端口 80 而不是端口 443,但这里不是这种情况(据我所知),因为确切的相同的代码(见下面的最小示例)适用于我试过的大多数网站。

我尝试使用 wireshark 进行检查,看看是否可以发现有错误的情况和没有错误的情况之间的区别。这是我发现的:

根据这些观察,我知道我的代码能够使用 TLSv1.3,而且我认为使用 TLSv1 是问题所在。因此,我试图强制 asio 在创建上下文时使用版本 > 1 的 TLS 和 asio::ssl::context::tlsv13_client,或者通过将 asio::ssl::context::no_tlsv1 添加到 set_options,但 wireshark 仍然显示使用了 TLSv1 协议.

关于第二点,我对网络的东西不太熟悉,所以我不确定我能得出什么结论,或者即使它与问题相关。

最小工作示例:

#include <asio.hpp>
#include <asio/ssl.hpp>

#include <iostream>

int main(int argc, char* argv[])
{
    try
    {
        asio::io_context io_context;
        asio::ip::tcp::resolver resolver(io_context);
        asio::ip::tcp::resolver::results_type endpoints = resolver.resolve("google.com", "https"); //not working with "api.minecraftservices.com" for example

        asio::ssl::context ctx(asio::ssl::context::sslv23); // also tried tlsv13_client to force v1.3, without success
        ctx.set_default_verify_paths();
        ctx.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::verify_none);

        asio::ssl::stream<asio::ip::tcp::socket> socket(io_context, ctx);
        socket.set_verify_mode(asio::ssl::verify_none);
        socket.set_verify_callback([](bool, asio::ssl::verify_context&) {return true; });
        asio::connect(socket.lowest_layer(), endpoints);
        socket.handshake(socket.client);
    }
    catch (const std::exception& e)
    {
        std::cout << e.what() << std::endl;
        return -1;
    }

    return 0;
}

您需要更具体地说明您尝试连接的服务器:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <iostream>

namespace ssl = boost::asio::ssl;
using boost::asio::ip::tcp;

int main() {
    try {
        boost::asio::io_context io_context;
        tcp::resolver           resolver(io_context);

        ssl::context ctx(ssl::context::sslv23); // also tried tlsv13_client to
                                                // force v1.3, without success
        ctx.set_default_verify_paths();
        ctx.set_options(ssl::context::default_workarounds |
                        ssl::context::verify_none);

        ssl::stream<tcp::socket> socket(io_context, ctx);
        socket.set_verify_mode(ssl::verify_none);
        socket.set_verify_callback([](auto&&...) { return true; });

#ifndef COLIRU
        connect(socket.lowest_layer(), resolver.resolve("d7uri8nf7uskq.cloudfront.net", "https"));
#else
        socket.lowest_layer().connect({boost::asio::ip::address_v4::from_string("65.9.84.220"), 443});
#endif
        std::cout << "Connected to " << socket.lowest_layer().remote_endpoint() << "\n";
        socket.handshake(socket.client);
    } catch (std::exception const& e) {
        std::cout << e.what() << std::endl;
        return -1;
    }
}

版画

Connected to 65.9.84.220:443

更新

确实,使用 api.minecraftservices.com:443 会打印

Connected to 65.9.78.95:443
handshake: wrong version number (SSL routines, ssl3_get_record) [asio.ssl:336130315]

原来你需要SNI:

SSL_set_tlsext_host_name(socket.native_handle(), "api.minecraftservices.com");

然后它就可以工作了(使用不同的 IP 分辨率)

Connected to 65.9.78.23:443