Search code examples
c++boost-asiocpprest-sdk

What's wrong with the cpprestsdk https handshake


I applied ssl but the result is not satisfactory. There is no key change between the server and the client when viewed with wireshark.

Communication is possible, but not satisfactory. The server is Linux and the client is using Chrome. I know that the normal ssl communication message is

enter image description here

"client key exchange, password change specification, encrypted handshake message"

and

"server hello, certificate, server key exchange, server hello done". but it is invisible to me.

code

#include <iostream>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>

using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;

int main(){
    http_listener_config listen_config;
    listen_config.set_ssl_context_callback([](boost::asio::ssl::context &ctx)
    {

        ctx.set_options(
            boost::asio::ssl::context::default_workarounds
            | boost::asio::ssl::context::no_sslv2
            | boost::asio::ssl::context::no_tlsv1
            | boost::asio::ssl::context::no_tlsv1_1
            | boost::asio::ssl::context::single_dh_use);
        ctx.set_password_callback([](std::size_t max_length, boost::asio::ssl::context::password_purpose purpose)
        {
           return "password";
        });
        ctx.use_certificate_chain_file("rootca.crt");
        ctx.use_private_key_file("rootca.key", boost::asio::ssl::context::pem);
        ctx.use_tmp_dh_file("dh2048.pem");
    });


    listen_config.set_timeout(utility::seconds(10));
    http_listener listener(U("https://0.0.0.0:10022"), listen_config);        //Server URL, Port .
        listener.support(methods::GET, [](http_request req){
        auto j = json::value::object();
        auto path = req.request_uri().path();
        std::cout << path << std::endl;
        j[U("one")] = json::value::string(U("asdfasefasdfaefasdfasefasdfefasefasefaqf3wfsefasdfasefasdfzsfzdfaesfzsefzsdfzsef"));
        req.reply(status_codes::OK, j);                     
        });
    
    listener.open().then([&listener](){std::cout << (U("\n start!!\n"));}).wait();    //Server open
    while(true);
    listener.close();
    return 0;
    
}

Packet when client connects to server. enter image description here

Is there a problem with this code? or is this normal?


Solution

  • No this is not ok:

    Doing a

    openssl s_client -connect :10022 -debug -showcerts -state
    

    Shows

    CONNECTED(00000005)
    write to 0x5614f8b23690 [0x5614f8b33620] (311 bytes => 311 (0x137))
    0000 - 16 03 01 01 32 01 00 01-2e 03 03 79 fe 76 37 2b   ....2......y.v7+
    0010 - 9f bf e7 62 51 34 7d 4a-00 5e 1f ed 55 64 61 e1   ...bQ4}J.^..Uda.
    0020 - 44 ce a0 c0 31 eb 3a b0-80 78 87 20 70 c4 1f 56   D...1.:..x. p..V
    0030 - 62 da 74 b7 d7 6a 31 50-0c 8c 90 46 23 c6 59 13   b.t..j1P...F#.Y.
    0040 - 17 6c 67 8f e3 9a 85 77-ab 6e c2 a3 00 3e 13 02   .lg....w.n...>..
    0050 - 13 03 13 01 c0 2c c0 30-00 9f cc a9 cc a8 cc aa   .....,.0........
    0060 - c0 2b c0 2f 00 9e c0 24-c0 28 00 6b c0 23 c0 27   .+./...$.(.k.#.'
    0070 - 00 67 c0 0a c0 14 00 39-c0 09 c0 13 00 33 00 9d   .g.....9.....3..
    0080 - 00 9c 00 3d 00 3c 00 35-00 2f 00 ff 01 00 00 a7   ...=.<.5./......
    0090 - 00 00 00 0e 00 0c 00 00-09 6c 6f 63 61 6c 68 6f   .........localho
    00a0 - 73 74 00 0b 00 04 03 00-01 02 00 0a 00 0c 00 0a   st..............
    00b0 - 00 1d 00 17 00 1e 00 19-00 18 00 23 00 00 00 16   ...........#....
    00c0 - 00 00 00 17 00 00 00 0d-00 30 00 2e 04 03 05 03   .........0......
    00d0 - 06 03 08 07 08 08 08 09-08 0a 08 0b 08 04 08 05   ................
    00e0 - 08 06 04 01 05 01 06 01-03 03 02 03 03 01 02 01   ................
    00f0 - 03 02 02 02 04 02 05 02-06 02 00 2b 00 09 08 03   ...........+....
    0100 - 04 03 03 03 02 03 01 00-2d 00 02 01 01 00 33 00   ........-.....3.
    0110 - 26 00 24 00 1d 00 20 a1-ab 9c 0d 7e e9 80 84 ba   &.$... ....~....
    0120 - 0a 5a 71 20 7d 59 cd d9-24 9c de 9a 05 9d a2 78   .Zq }Y..$......x
    0130 - 0d 0d 79 a1 3b 65 58                              ..y.;eX
    read from 0x5614f8b23690 [0x5614f8b2a313] (5 bytes => 0 (0x0))
    ---
    no peer certificate available
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 0 bytes and written 311 bytes
    Verification: OK
    ---
    New, (NONE), Cipher is (NONE)
    Secure Renegotiation IS NOT supported
    Compression: NONE
    Expansion: NONE
    No ALPN negotiated
    Early data was not sent
    Verify return code: 0 (ok)
    ---
    read from 0x5614f8b23690 [0x5614f8b18d80] (8192 bytes => 0 (0x0))
    

    So I was getting none-secure fallback. Turns out unhandled exceptions cause really degenerate behaviour. Adding some exception handling reveals:

    ERROR: use_certificate_chain_file: No such file or directory
    

    Well, of course. Adding those:

    NOTE: It seems unlikely that you'd actually use a rootca for the sever. Maybe change the name of the file to reflect what it contains, or reconsider the cert you use for a server purpose.

            ctx.set_password_callback(
                [](std::size_t /*max_length*/,
                   ssl::context::password_purpose /*purpose*/) {
                    return "test";
                });
            ctx.use_certificate_chain_file("server.pem");
            ctx.use_private_key_file("server.pem", ssl::context::pem);
            ctx.use_tmp_dh_file("dh2048.pem");
    

    Note, I used the key/dh params from the boost SSL samples, so the passphrase is test

    Now, a simple GET request works, either with s_client again or using something like wget:

    wget --no-check-certificate https://localhost:10022/ -O - -q
    {"one":"asdfasefasdfaefasdfasefasdfefasefasefaqf3wfsefasdfasefasdfzsfzdfaesfzsefzsdfzsef"}
    

    Summary

    My assumption is that you, too, fell into the trap of not handling errors. I consider the "fail open" mode of the library a potential security issue - though all those parameters to the ssl::context might have been effectively asking for that.


    For reference, the full code I tested with in the end:

    #include <cpprest/http_listener.h>
    #include <cpprest/json.h>
    #include <iostream>
    
    using namespace web;
    using namespace web::http;
    using namespace web::http::experimental::listener;
    namespace net = boost::asio;
    namespace ssl = net::ssl;
    
    int main() {
        http_listener_config listen_config;
        listen_config.set_ssl_context_callback([](ssl::context& ctx) {
            try {
                std::clog << "set_ssl_context_callback" << std::endl;
                ctx.set_options(ssl::context::default_workarounds |
                                ssl::context::no_sslv2 | ssl::context::no_tlsv1 |
                                ssl::context::no_tlsv1_1 |
                                ssl::context::single_dh_use);
                ctx.set_password_callback(
                    [](std::size_t /*max_length*/,
                       ssl::context::password_purpose /*purpose*/) {
                        return "test";
                    });
                ctx.use_certificate_chain_file("server.pem");
                ctx.use_private_key_file("server.pem", ssl::context::pem);
                ctx.use_tmp_dh_file("dh2048.pem");
                std::clog << "leave set_ssl_context_callback" << std::endl;
            } catch (std::exception const& e) {
                std::clog << "ERROR: " << e.what() << "\n";
            }
        });
    
        listen_config.set_timeout(utility::seconds(10));
        http_listener listener(U("https://localhost:10022"),
                               listen_config); // Server URL, Port .
    
        listener.support(methods::GET, [](http_request req) {
            std::clog << "enter handler" << std::endl;
            auto j = json::value::object();
            auto path = req.request_uri().path();
            std::clog << path << std::endl;
            j[U("one")] =
                json::value::string(U("asdfasefasdfaefasdfasefasdfefasefasefaqf3wfs"
                                      "efasdfasefasdfzsfzdfaesfzsefzsdfzsef"));
            req.reply(status_codes::OK, j).wait();
            std::clog << "leave handler" << std::endl;
        });
    
        listener.open()
            .then([&listener] { std::clog << (U("\n start!!\n")); })
            .wait(); // Server open
        while (true)
            ;
        listener.close(); // huh
    }