Katie
Katie

Reputation: 3707

SSL3_READ_BYTES:tlsv1 alert internal error, secure sockets, OpenSSL, C++, getting HTTPS page

I want to write a simple application, which grabs the content of the HTTPS page: https://httpbin.org/html.

I know how to access its insecure (HTTP) version (http://httpbin.org/html), I just need to send a request using sockets:

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);

And it works. However, when using secure sockets (#include <openssl/ssl.h>) I got the following error: error:14094438:SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error when trying result = SSL_connect(ssl);

Here's the code:

#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;
}

Upvotes: 7

Views: 37445

Answers (3)

Sean B
Sean B

Reputation: 11

In case it is ever useful to anyone, I had the same error (irregularly ) in my C++ program and it was driving me mad:

error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version I tried all combinations of OpenSSL, different TLS versions etc, etc.

In the end, I found the Ping I had implemented ( to check the socket was still up ) was being called while another thread's SSL_read / SSL_write() was active on the same SSL object.

Upvotes: 1

Buffoonism
Buffoonism

Reputation: 1868

This error is generated for a handshake failure, which is caused by a miss-match between what the client offers and what the server accepts.

For some situations, openssl will do a pretty good job of negotiating a working set between the client and server. For other situations, it's a hard failure and you will need to ammend your code. I have seen this error generated due to:

Protocol and cipher missmatch

SSL_set_min_proto_version()
SSL_set_max_proto_version()
SSL_set_cipher_list()

SNI required by the server but not offered by the client

SSL_set_tlsext_host_name()

Reused session offered by client but sessions not implemented on server

SSL_set_session()

Upvotes: 0

Steffen Ullrich
Steffen Ullrich

Reputation: 123461

... SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error when trying result = SSL_connect(ssl);

This means that the server sends a TLSv1 alert back probably because it does not like the information send by the client in the handshake (ClientHello). This might be because the client only offers ciphers which the server does not supports, that the client uses a protocol version not supported or very often also because the server requires the TLS SNI extension to determine which certificate to provide to the client. Testing with other tools indicates that this is actually the case. The problem thus can be solved in your code by using the SNI extension:

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);

Upvotes: 6

Related Questions