使用 OpenSSL 滚动我自己的 SSL,不工作

Rolling my own SSL with OpenSSL, not working

我已经阅读了所有关于不这样做的注意事项,但是......我需要这样做。我需要能够从 C 程序向 SSL/TLS 安全服务器发送 POST 消息,但它无法正常工作。

我使用相同的代码通过 SSL 获取数据,但我不能 POST。每次尝试 returns 一个 400 BAD REQUEST。我可以通过另一个 ReST 客户端(奇怪的是称为 "Rest Client")使用完全相同的数据并且它有效。我可以通过 curl 运行 相同的命令并且它有效。但是通过 C/openssl,相同的响应。

OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();

    /* https://www.openssl.org/docs/ssl/SSL_CTX_new.html */
    const SSL_METHOD* method = SSLv23_method();

    /* http://www.openssl.org/docs/ssl/ctx_new.html */
    ctx = SSL_CTX_new(method);

    /* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);

    /* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
    SSL_CTX_set_verify_depth(ctx, 5);

    const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
    long old_opts = SSL_CTX_set_options(ctx, flags);
    UNUSED(old_opts);

    /* http://www.openssl.org/docs/ssl/SSL_CTX_set_default_verify_paths.html */
    res = SSL_CTX_set_default_verify_paths(ctx);


    /* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
    web = BIO_new_ssl_connect(ctx);

    /* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
    res = BIO_set_conn_hostname(web, HOST_NAME ":" HOST_PORT);

    /* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
    /* This copies an internal pointer. No need to free.  */
    BIO_get_ssl(web, &ssl);

    /* https://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_CONTEXTS */
    /* https://www.openssl.org/docs/ssl/SSL_CTX_set_cipher_list.html            */
    res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);

    /* No documentation. See the source code for tls.h and s_client.c */
    res = SSL_set_tlsext_host_name(ssl, HOST_NAME);

    /* https://www.openssl.org/docs/crypto/BIO_s_file.html */
    out = BIO_new_fp(stdout, BIO_NOCLOSE);

    /* https://www.openssl.org/docs/crypto/BIO_s_connect.html */
    res = BIO_do_connect(web);

    /* https://www.openssl.org/docs/crypto/BIO_f_ssl.html */
    res = BIO_do_handshake(web);

    /* Step 1: verify a server certifcate was presented during negotiation */
    /* https://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html          */
    X509* cert = SSL_get_peer_certificate(ssl);

    /* Step 2: verify the result of chain verifcation             */
    /* http://www.openssl.org/docs/ssl/SSL_get_verify_result.html */
    /* Error codes: http://www.openssl.org/docs/apps/verify.html  */
    res = SSL_get_verify_result(ssl);
    certname = X509_NAME_new();
    certname = X509_get_subject_name(cert);
    BIO_printf(out, "Displaying the certificate subject data:\n");
    X509_NAME_print_ex(out, certname, 0, 0);
    BIO_printf(out, "\n");
    sprintf(message, "POST /api/buckets\r\nHTTP/1.1\r\nX-IS-AccessKey: accessKey\r\nContent-Type: application/json\r\nContent-Length: 66\r\n\r\n{\"bucketKey\": \"MyBucket\", \"bucketName\": \"My Bucket\"}\r\n\r\n");
    BIO_puts(web, message);
    BIO_puts(out, message);

    int len = 0;
    do {
        char buff[1536] = {};

        /* https://www.openssl.org/docs/crypto/BIO_read.html */
        len = BIO_read(web, buff, sizeof(buff));

        if(len > 0)
            BIO_write(out, buff, len);

        /* BIO_should_retry returns TRUE unless there's an  */
        /* error. We expect an error when the server        */
        /* provides the response and closes the connection. */

    } while (len > 0 || BIO_should_retry(web));

    ret = 0;

} while (0);

事实上,这确实成功地打开了一个连接,并检索了一个证书,并验证了它,等等。

verify_callback (depth=1)(preverify=0)
Issuer (cn): DigiCert Global Root CA
Subject (cn): DigiCert SHA2 Secure Server CA
Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
verify_callback (depth=0)(preverify=1)
Issuer (cn): DigiCert SHA2 Secure Server CA
Subject (cn): *.initialstate.com
Subject (san): *.initialstate.com
Subject (san): initialstate.com
Displaying the certificate subject data:
    C=US, ST=TN, L=Brentwood, O=Initial State Technologies, Inc, CN=*.initialstate.com

这似乎行得通。如果我从 www.google.com 执行一个简单的 "GET /",我会得到相同的证书响应(好吧,使用来自 google 的适当证书)并且我可以很好地恢复所有数据。

所以这似乎与实际 POSTing 数据有关。

如果有人能指出我方法的错误——不仅仅是我这样做的愚蠢行为! -- 非常感谢!

..."POST /api/buckets\r\nHTTP/1.1\r\n

路径和 HTTP 版本之间的换行符错误,必须是 space。这意味着你发送到这里

  POST /api/buckets
  HTTP/1.1

而不是

  POST /api/buckets HTTP/1.1