我使用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/也是如此。当然这不对吗?
答案 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地址似乎已经解决了。但这似乎与症状有关。