Boost Asio https синхронный вызов - код ошибки 400 неверный запрос

мы переходим с http на https boost asio sysnchornous call, и я использую приведенный ниже код, чтобы сделать синхронный вызов https с проверкой сертификата ssl. мы получили сертификат клиента, выданный центром сертификации, и загрузили его в формате .pem. у нас есть следующие вопросы:

1.) как загрузить сертификаты в boost asio; мы можем загрузить файл сертификата с путем, как показано ниже:

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.) какова цель одноранговой проверки при синхронном вызове https; Можем ли мы выполнить рукопожатие без одноранговой проверки, как показано ниже?

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.) Я получаю код ошибки Bad request 400, когда я нажимаю URL-адрес конечной точки с проверкой SSL. Пожалуйста, проверьте приведенный ниже код и дайте мне знать, если мне не хватает части сертификата ssl (примечание: заголовок запроса и сообщение работали нормально, прежде чем переходить на 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);

Спасибо


person Krishna Moorthy    schedule 07.08.2020    source источник
comment
Старайтесь задавать только 1 вопрос за раз. Остальные, без сомнения, являются дубликатами существующих (отвеченных) вопросов и полностью отличаются от заглавия.   -  person sehe    schedule 07.08.2020


Ответы (1)


Ваш запрос плохой :)

HTTP требует окончания строки CR+LF. Таким образом, везде, где вы используете только \n, он должен быть \r\n.

Полный рабочий образец

Я заполнил ваш образец, чтобы протестировать его. Возможно, я (?) ответил на ваш вопрос о сертификатах - или частично - с помощью

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

При этом используется место, где большинство систем Linux будут хранить хранилище сертификатов по умолчанию, то есть сертификаты, которые обычно являются доверенными для всей системы.

Листинг:

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

Он использует пример онлайн-сервиса postman и выводит:

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"}

Вероятно, ожидается stream truncated: https://github.com/boostorg/beast/issues/38< /а>

person sehe    schedule 07.08.2020
comment
Привет, я попробовал тот же запрос, используя http, и мы получили ответ от URL-адреса конечной точки http. это специфично для запроса HTTPS? - person Krishna Moorthy; 07.08.2020
comment
Это может зависеть только от сервера, поэтому, если на HTTPS-сервере работает другая реализация, это может быть. В любом случае нет необходимости отправлять неверный запрос, если вы можете это исправить :) Я обновил ответ, включив в него рабочий пример, который я тестировал, включая вывод. - person sehe; 07.08.2020
comment
Спасибо за помощь Сехе. Я попробовал, как вы предложили (заменив \n на \r\n), и теперь код ошибки изменился с 400 (неверный запрос) на 404, и завтра я проверю эту ошибку в целевой системе. у нас есть стандарт организации для получения клиентских сертификатов, которые должны быть выданы центром сертификации; поэтому я использовал ctx.load_verify_file(/home/sysusr/cert/ca.pem, ec); для загрузки клиентских сертификатов на основе того, какая целевая система будет проверять, выдана ли она правильным доверенным органом. это нормально? - person Krishna Moorthy; 09.08.2020
comment
Звучит неплохо. Если вам нужно несколько ЦС, может быть более естественным использовать add_verify_path с вашим собственным каталогом (не забудьте c_rehash!). Кроме того, клиентские сертификаты являются другая вещь на жаргоне SSL/TLS. Функции verify применяются только для проверки сертификата удаленной конечной точки. - person sehe; 09.08.2020
comment
нам нужно использовать сертификат клиента, а также корневые/промежуточные сертификаты ЦС, как вы предложили, нам нужно использовать add_verify_path, и не могли бы вы предложить, как сгенерировать хеш-значение для файлов формата .pem и .cer в redhat linux? - person Krishna Moorthy; 10.08.2020
comment
c_rehash является частью openssl и входит в его состав во всех известных мне дистрибутивах Linux. - person sehe; 10.08.2020
comment
спасибо, я использовал openssl для преобразования из der в pem, а затем из pem в хэш command-script-on-linux" title="что эквивалентно командному сценарию unix c rehash в linux">stackoverflow.com/questions/25889341/ . Я постараюсь попасть и сообщить вам результат. - person Krishna Moorthy; 10.08.2020
comment
Привет, Сехе, мы получили ответ от сервера, но при чтении его из scoket я получаю короткую ошибку чтения. из-за чего мы не можем разобрать ответное сообщение. - person Krishna Moorthy; 12.08.2020
comment
Короткое чтение — это обычное состояние, которое вам, вероятно, просто нужно игнорировать. Я связал вас с обширным обсуждением этой темы в последнем абзаце. - person sehe; 12.08.2020
comment
спасибо сехе. Я проигнорировал на основе значения ошибки и имени категории ошибки. Не могли бы вы сообщить мне, как закрыть сокет, отключив соединение рукопожатия ssl. - person Krishna Moorthy; 12.08.2020
comment
Большое спасибо за вашу помощь. Теперь мы можем нажать URL-адрес https и проанализировать ответ после выключения, как вы указали в приведенном выше примере (socket.lowest_layer().shutdown(tcp::socket::shutdown_send);). - person Krishna Moorthy; 12.08.2020
comment
Привет, Сехе, мы получаем ответ JSON, и он разбивается на несколько строк; я пытаюсь удалить символ новой строки и сделать его одной строкой для его анализа. но в конце добавляется дополнительный 0 (это из-за короткой ошибки), и иногда я получаю неизвестное исключение перед синтаксическим анализом (у меня есть сомнения в закрытии сокета и рукопожатии ssl). Не могли бы вы предложить некоторые идеи по этому поводу? - person Krishna Moorthy; 13.08.2020
comment
Прежде всего: ваш протокол ограничен/обрамлен? Если это так, используйте этот фрейм для чтения всего объекта JSON. Если нет, то было бы разумно, чтобы EOF сигнализировал конец сообщения. поэтому просто замените read_until на read с достаточной емкостью буфера. - person sehe; 13.08.2020
comment
Сначала я пытаюсь прочитать ответ до новой строки, чтобы получить статус ответа boost::asio::read_until(socket, response_, \r\n); если статус равен 200, то мы передадим остальное содержимое из сокета boost::asio::read(socket, response_, boost::asio::transfer_all(), error);. - person Krishna Moorthy; 13.08.2020
comment
Как я уже сказал, ранее ответ был получен с символом новой строки (\r\n), который я удаляю (система назначения говорит, что они отправляют ответ в одной строке), и помимо этого мы могли видеть, что есть некоторые дополнительные символов в ответе JSON, из-за которого анализатор JSON не анализирует ответ (decisionKey21f0: SGMCEType), здесь вы можете видеть, что добавляется 21fo is extrac charcter. Не могли бы вы предложить какие-либо идеи, как мы можем гарантировать, что полученный ответ является точным. - person Krishna Moorthy; 13.08.2020
comment
Ответ, скорее всего, получен правильно, но вы не обрабатываете его точно. Вы можете опубликовать другой вопрос с автономным примером (MCVE или SSCCE — начните с моего ответа, если вам нужна помощь), - person sehe; 13.08.2020