首页 > 解决方案 > C 使用 openssl 执行 HTTPS 请求

问题描述

我是 C 语言的新手,我正在尝试在 Linux Ubuntu 18.04 中使用 OpenSSL 执行简单的 SSL GET 请求。我在Simple C example of doing an HTTP POST and consumption the response 中找到了这个

#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>

#define HOST "www.google.com"
#define PORT "443"

int main() {

    //
    //  Initialize the variables
    //
    BIO* bio;
    SSL* ssl;
    SSL_CTX* ctx;

    //
    //   Registers the SSL/TLS ciphers and digests.
    //
    //   Basically start the security layer.
    //
    SSL_library_init();

    //
    //  Creates a new SSL_CTX object as a framework to establish TLS/SSL
    //  or DTLS enabled connections
    //
    ctx = SSL_CTX_new(SSLv23_client_method());

    //
    //  -> Error check
    //
    if (ctx == NULL)
    {
        printf("Ctx is null\n");
    }

    //
    //   Creates a new BIO chain consisting of an SSL BIO
    //
    bio = BIO_new_ssl_connect(ctx);

    //
    //  Use the variable from the beginning of the file to create a 
    //  string that contains the URL to the site that you want to connect
    //  to while also specifying the port.
    //
    BIO_set_conn_hostname(bio, HOST ":" PORT);

    //
    //   Attempts to connect the supplied BIO
    //
    if(BIO_do_connect(bio) <= 0)
    {
        printf("Failed connection\n");
        return 1;
    }
    else
    {
        printf("Connected\n");
    }

    //
    //  The bare minimum to make a HTTP request.
    //
    char* write_buf = "GET / HTTP/1.1\r\n"
                      "Host: " HOST "\r\n"
                      "Connection: close\r\n"
                      "\r\n";

    //
    //   Attempts to write len bytes from buf to BIO
    //
    if(BIO_write(bio, write_buf, strlen(write_buf)) <= 0)
    {
        //
        //  Handle failed writes here
        //
        if(!BIO_should_retry(bio))
        {
            // Not worth implementing, but worth knowing.
        }

        //
        //  -> Let us know about the failed writes
        //
        printf("Failed write\n");
    }

    //
    //  Variables used to read the response from the server
    //
    int size;
    char buf[1024];

    //
    //  Read the response message
    //
    for(;;)
    {
        //
        //  Get chunks of the response 1023 at the time.
        //
        size = BIO_read(bio, buf, 1023);

        //
        //  If no more data, then exit the loop
        //
        if(size <= 0)
        {
            break;
        }

        //
        //  Terminate the string with a 0, to let know C when the string 
        //  ends.
        //
        buf[size] = 0;

        //
        //  ->  Print out the response
        //
        printf("%s", buf);
    }

    //
    //  Clean after ourselves
    //
    BIO_free_all(bio);
    SSL_CTX_free(ctx);

    return 0;
}

此代码运行良好,但对于某些网站,例如ghostbin.co,连接失败......是什么原因造成的?我使用gcc test.c -o test -lssl -lcrypto. 我正在使用 OpenSSL 1.1.1 2018 年 9 月 11 日

标签: chttphttpsopenssl

解决方案


几乎总是当 OpenSSL 例程返回错误指示时,您可以并且应该从错误堆栈中获取附加信息;请参阅https://www.openssl.org/docs/faq.html#PROG8和手册页,例如
https://www.openssl.org/docs/manmaster/man3/ERR_print_errors.html
https://www.openssl。 org/docs/manmaster/man3/ERR_get_error.html
https://www.openssl.org/docs/manmaster/man3/ERR_error_string.html
https://www.openssl.org/docs/manmaster/man3/ERR_load_crypto_strings.html

但是在这种情况下,它只会告诉您服务器通过发送警报 40 中止了握手,这是 SSL/TLS 中信息量较少的警报代码之一。需要一点知识才能在 CloudFlare 上找到 ghostbin.co,它要求连接使用服务器名称指示(也称为 SNI)——本世纪许多 SSL/TLS 服务器都是如此(因为顶级 IPv4 耗尽),尤其是那些像 CDN 服务或共享主机一样共享。OpenSSL API 确实允许客户端发送 SNI,但仅使用每个连接的 SSL 对象而不是 SSL_CTX,并且使用(相当陈旧且相当老旧的)BIO_ssl,SSL 对象被隐藏,因此您需要BIO_get_ssl(bio,&sslptr)然后SSL_set_tlsext_host_name(sslptr,name)(尽管我注意到手册页的 const 错误;这确实是一个使用宏SSL_ctrl它需要一个绝对非常量的指针)。


推荐阅读