SSL3_READ_BYTES:tlsv1 警报内部错误、安全套接字、OpenSSL、C++、获取 HTTPS 页面

Kat*_*tie 4 c++ sockets openssl

我想编写一个简单的应用程序,它抓取 HTTPS 页面的内容:https://httpbin.org/html

我知道如何访问它的不安全(HTTP)版本(http://httpbin.org/html),我只需要使用套接字发送一个请求:

std::stringstream ss;
ss << "GET /html HTTP/1.1" << "\r\n"
<< "Host: httpbin.org\r\n"
<< "Connection: close"
<< "\r\n\r\n";
std::string request = ss.str();

// socket creation, connect

send(sock, request.c_str(), request.size(), 0);
Run Code Online (Sandbox Code Playgroud)

它有效。但是,在使用安全套接字 ( #include <openssl/ssl.h>) 时出现以下错误:error:14094438:SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error尝试时result = SSL_connect(ssl);

这是代码:

#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string>
#include <iostream>
#include <sstream>

int main(int argc, char **argv)
{
    int    sock;
    char   buff[1024];

    struct sockaddr_in  address;
    struct hostent     *host = NULL;

    std::string servername = "httpbin.org";

    if (!inet_aton(servername.c_str(), &address.sin_addr))
        if (host = gethostbyname(servername.c_str()))
            address.sin_addr = *(struct in_addr*)host->h_addr;
        else
            return -1;

    address.sin_port   = htons(443);
    address.sin_family = AF_INET;

    std::stringstream ss;
    ss << "GET /html HTTP/1.1" << "\r\n"
    << "Host: httpbin.org\r\n"
    << "Connection: close"
    << "\r\n\r\n";
    std::string request = ss.str();


    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1)
    {
        if (connect(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != -1)
        {
            SSL_library_init();
            OpenSSL_add_all_algorithms();
            SSL_load_error_strings();
            SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method());
            if (!ctx)
                return -1;

            int result = -1;
            SSL *ssl = SSL_new(ctx);
            SSL_set_fd(ssl, sock);
            result = SSL_connect(ssl);

            if (result == 0)
            {
                long error = ERR_get_error();
                const char* error_str = ERR_error_string(error, NULL);
                printf("%s\n", error_str);
                return 0;
            }

            SSL_write(ssl, request.c_str(), request.size());
            SSL_read(ssl, buff, 1024);
            printf("%s\n", buff);
            memset(buff, 0, 1024);

            SSL_free(ssl);
            SSL_CTX_free(ctx);
            close(sock);
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ich 6

...SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error当尝试result = SSL_connect(ssl);

这意味着服务器发送回 TLSv1 警报可能是因为它不喜欢客户端在握手(ClientHello)中发送的信息。这可能是因为客户端只提供服务器不支持的密码,客户端使用的协议版本不受支持,或者经常因为服务器需要TLS SNI 扩展来确定向客户端提供哪个证书。使用其他工具进行测试表明情况确实如此。因此,可以通过使用 SNI 扩展在您的代码中解决该问题:

SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);

// ADD THIS
SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*)servername.c_str());

result = SSL_connect(ssl);
Run Code Online (Sandbox Code Playgroud)