2

we are migrating from http to https boost asio sysnchornous call and I am using the below code to make https synchoronous call with ssl certificate validation. we got our client certificate issued by certiticate authority and we downloaded it in .pem format. we have the following questions:

1.) how to load the certificates in boost asio; can we load the certificate file with the path as below:

boost::asio::streambuf response_;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
//ctx.set_default_verify_paths();
**ctx.load_verify_file("/tmp/cacert.pem");**
ctx.set_options(boost::asio::ssl::context::default_workarounds |
       boost::asio::ssl::context::no_sslv2 |
       boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service,ctx);

2.)what is the purpose of peer verification in synchoronous https call; can we make handshake without peer verification as like below?

tcp::resolver resolver(io_service);
tcp::resolver::query query(hostname, port_no);

tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;

boost::system::error_code error  = boost::asio::error::host_not_found;
boost::asio::connect(socket.lowest_layer(), endpoint_iterator, error);
socket.handshake(boost::asio::ssl::stream_base::client);

3.) I am getting Bad request Error code 400 when i am hitting endpoint url with ssl verification. Please verify the below code and let me know if i am missing related to ssl certificate part(note: request header and message worked fine before changing to https):

boost::asio::streambuf response_;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
ctx.load_verify_file("/tmp/cacert.pem");
ctx.set_options(boost::asio::ssl::context::default_workarounds |
       boost::asio::ssl::context::no_sslv2 |
       boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service,ctx);

std::ostream request_stream(&request_);
request_stream << "POST " << server_endpoint << " HTTP/1.1\n";
request_stream << "Host: " << hostname << "\n";
request_stream << "Accept: */*\n";
request_stream << authorization_token << "\n";
request_stream << client_name << "\n";
request_stream << "Content-Length: " << req_str.length() << "\n";
request_stream << "Content-Type: application/x-www-form-urlencoded \n";
request_stream << "Connection: close\r\n\r\n";
request_stream << req_str << "\n";
tcp::resolver resolver(io_service);
tcp::resolver::query query(hostname, port_no);

tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;

boost::system::error_code error  = boost::asio::error::host_not_found;
boost::asio::connect(socket.lowest_layer(), endpoint_iterator, error);
socket.handshake(boost::asio::ssl::stream_base::client);

Thanks

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Try to ask only 1 question at a time. The others are, no doubt, duplicates of existing (answered) questions, and totally different from the title matter. – sehe Aug 07 '20 at 14:55

1 Answers1

0

Your request is bad :)

HTTP requires CR+LF line ends. So, everywhere you use \n alone it needs to be \r\n.

Complete Working Sample

I completed your sample in order to test it. I may (?) have answered your question about certificates - or partly - by using

    //ctx.load_verify_file("ca.pem");
    ctx.add_verify_path("/etc/ssl/certs");

This uses the location where most Linux systems will store the default certificate store, meaning the certificates that are typically trusted system-wide.

Listing:

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <iostream>
using boost::asio::ip::tcp;

// https://postman-echo.com/post see https://docs.postman-echo.com/?version=latest
static const std::string
    server_endpoint = "/post",
    hostname = "postman-echo.com",
    port_no = "443",
    authorization_token =
        "Auth: "
        "c3RhdGljIGNvbnN0IHN0ZDo6c3RyaW5nIGF1dGhvcml6YXRpb"
        "25fdG9rZW4gPSAiQXV0aDogIj"
        "sK",
    client_name = "User-Agent: demo program 0.01",
    req_str = R"(name=blabla&password=bloblo)";

int main() {
    boost::asio::io_service io_service;
    boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);

    ctx.set_verify_mode(boost::asio::ssl::verify_peer);
    //ctx.load_verify_file("ca.pem");
    ctx.add_verify_path("/etc/ssl/certs");

    ctx.set_options(boost::asio::ssl::context::default_workarounds |
                    boost::asio::ssl::context::no_sslv2 |
                    boost::asio::ssl::context::no_sslv3);
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service,
                                                                  ctx);

    {
        tcp::resolver resolver(io_service);
        tcp::resolver::query query(hostname, port_no);

        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        tcp::resolver::iterator end;

        boost::system::error_code error = boost::asio::error::host_not_found;
        boost::asio::connect(socket.lowest_layer(), endpoint_iterator, error);
    }

    {
        boost::asio::streambuf request_;
        socket.handshake(boost::asio::ssl::stream_base::client);
        {
            std::ostream request_stream(&request_);
            request_stream << "POST " << server_endpoint << " HTTP/1.1\r\n";
            request_stream << "Host: " << hostname << "\r\n";
            request_stream << "Accept: */*\r\n";
            request_stream << authorization_token << "\r\n";
            request_stream << client_name << "\r\n";
            request_stream << "Content-Length: " << req_str.length() << "\r\n";
            request_stream << "Content-Type: application/x-www-form-urlencoded \r\n";
            request_stream << "Connection: close\r\n\r\n";
            request_stream << req_str << "\r\n";
        } // forces flush()
        //std::cout << &request_;
        //std::cout << "--------" << std::endl;

        write(socket, request_);
        //socket.lowest_layer().shutdown(tcp::socket::shutdown_send);
    }

    {
        boost::asio::streambuf response_;
        boost::system::error_code ec;
        read(socket, response_, ec);
        
        std::cout << "ec: " << ec.message() << "\n";
        std::cout << &response_ << "\n";
    }
}

It uses the postman online sample service and prints:

ec: stream truncated
HTTP/1.1 200 OK
Date: Fri, 07 Aug 2020 16:23:04 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 519
Connection: close
ETag: W/"207-JMbCSlSXSCnZPMi2WQ8SuP+keys"
Vary: Accept-Encoding
set-cookie: sails.sid=s%3AFBof16WW2UeR2Si6dtf9WRUfKiJbpIhH.O2YgXPhClKJKnJ0bmTFuyl%2FyKNyS3oADFbDHHt4UKX8; Path=/; HttpOnly

{"args":{},"data":"","files":{},"form":{"name":"blabla","password":"bloblo"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-5f2d7fe8-0348dee860e746ac828f4d80","content-length":"27","accept":"*/*","auth":"c3RhdGljIGNvbnN0IHN0ZDo6c3RyaW5nIGF1dGhvcml6YXRpb25fdG9rZW4gPSAiQXV0aDogIjsK","user-agent":"demo program 0.01","content-type":"application/x-www-form-urlencoded"},"json":{"name":"blabla","password":"bloblo"},"url":"https://postman-echo.com/post"}

The stream truncated is probably expected: https://github.com/boostorg/beast/issues/38

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Hi I have tried the same request using http and we got response from http endpoint url. is it specific to Https request? – Krishna Moorthy Aug 07 '20 at 16:24
  • It can only depend on the server - so if the HTTPS server is running a different implementation, it might. In any case, it's not necessary to send an invalid request if you can fix it :) I've updated the answer to include a working example that I tested - including the output. – sehe Aug 07 '20 at 16:33
  • Thanks for your help Sehe. I have tried as you suggested(by replacing \n with \r\n) and now the error code got changed from 400(bad request) to 404 and will check with destination system about this error tomorrow. we have organisation standard to get the client certificates and need to be issued by certificate authority; so i have used ctx.load_verify_file("/home/sysusr/cert/ca.pem", ec); to load the client certificates based on which destination system will verify whether its issued by correct trusted authority. is this fine? – Krishna Moorthy Aug 09 '20 at 11:20
  • Sounds fine. If you need multiple CA's it could be more natural to use `add_verify_path` with your own directory (remember to [`c_rehash`](http://manpages.ubuntu.com/manpages/trusty/man1/c_rehash.1ssl.html)!). Also, "client certificates" are [a different thing in SSL/TLS jargon](https://en.wikipedia.org/wiki/Client_certificate#:~:text=In%20cryptography%2C%20a%20client%20certificate,assurances%20of%20a%20requester's%20identity.). The `verify` functions apply to verification of the remote endpoint's certificate only – sehe Aug 09 '20 at 12:54
  • we need to use client certificate as well as root/intermediate ca certificates as well, as you suggested we need to use add_verify_path and could you please suggest how to generate the hash value for .pem and .cer format files in redhat linux? – Krishna Moorthy Aug 10 '20 at 11:00
  • `c_rehash` is part of openssl and is packaged with it on all Linux distros I'm aware of – sehe Aug 10 '20 at 11:16
  • thanks sehe i have used openssl to convert from der to pem and then pem to hash https://stackoverflow.com/questions/25889341/what-is-the-equivalent-of-unix-c-rehash-command-script-on-linux . I will try to hit and let you know the result. – Krishna Moorthy Aug 10 '20 at 11:24
  • Hi Sehe, we got response from the server but while reading it from scoket , I am getting short read error. because of which we are not able to parse the response message. – Krishna Moorthy Aug 12 '20 at 12:35
  • The short read is a common condition that you probably just need to ignore. I linked you to an extensive discussion about that topic in the last paragraph – sehe Aug 12 '20 at 13:53
  • thanks sehe. I have ignored based on the error value and error category name. Could you please let me know how to close the socket by shutdown the ssl handshake connection. – Krishna Moorthy Aug 12 '20 at 18:31
  • thanks a lot sehe for your help. Now we are able to hit https url and able to parse the response after shutting down as you have given in the above example(socket.lowest_layer().shutdown(tcp::socket::shutdown_send);). – Krishna Moorthy Aug 12 '20 at 18:37
  • HI Sehe, we are getting the JSON response and its getting broken into multiple lines; i am trying to remove the newline character and to make it as single line to parse it. but at the end, extra 0 is added(is it because of short error) and sometimes i am getting unknown exception before parsing ( i have doubts in closing the socket and ssl handshake). Could you please suggest some ideas on this? – Krishna Moorthy Aug 13 '20 at 12:35
  • First thing is: is your protocol delimited/framed? If so, use that framing to read the entire JSON object. If not, it would be sensible for EOF to signal the end of the message. so just replace `read_until` with `read` with sufficient buffer capacity. – sehe Aug 13 '20 at 13:03
  • First I am trying to read the response until new line to get the response status boost::asio::read_until(socket, response_, "\r\n"); if status is 200 then we will transfer rest of the contents from socket boost::asio::read(socket, response_, boost::asio::transfer_all(), error);. – Krishna Moorthy Aug 13 '20 at 17:51
  • As i said earlier response is getting received with new line character(\r\n) which i am removing it(destination system is saying that they are sending the response in one line) and other than that we could see that there are some extra characters in the JSON response which is causing JSON parser not to parse the resposne("decisionKey"21f0:"SGMCEType") here you could see that 21fo is extrac charcter is getting added. Could you please suggest any idea how we can ensure that response received is accurate. – Krishna Moorthy Aug 13 '20 at 17:51
  • The response is likely received accurate, but you don't process it accurately. You can post another question with a selfcontained example ([MCVE](https://stackoverflow.com/help/minimal-reproducible-example) or [SSCCE](http://sscce.org) - start from my answer in case you need the help), – sehe Aug 13 '20 at 20:25