I have a file that tries to run a REST and a gRPC server together that should successfully handle both http and https requests.

However, when attempting to test the server with HTTPS requests, I encounter the below error:

REST Server started
Listening for requests at: https://localhost:8080/
E0221 10:21:11.667654416   53391 ssl_transport_security.cc:2258]       Could not create ssl context.
E0221 10:21:11.667749341   53391 ssl_security_connector.cc:270]        Handshaker factory creation failed with TSI_OUT_OF_RESOURCES.
E0221 10:21:11.667887612   53391 chttp2_server.cc:1045]                UNKNOWN:Unable to create secure server with credentials of type Ssl {file:"/opt/vcpkg/buildtrees/grpc/src/v1.51.1-1066d25324.clean/src/core/ext/transport/chttp2/server/chttp2_server.cc", file_line:1032, created_time:"2024-02-21T10:21:11.667802229+00:00"}
Failed to start gRPC server

Replicating the above Error

Libraries being used:

  • Openssl 3.2.1
  • grpc 1.41.1
  • protobuf 3.21.12
  • cryptopp 8.9.0
  • boost 1.71.0
  • cpprestsdk 2.10.15-1

Proto file for dummy gRPC server:

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

gRPC and REST server code:

#include <fstream>
#include <sstream>
#include <iostream>
#include <memory>
#include <string>
#include <cpprest/http_listener.h>
#include <cpprest/uri.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <cryptopp/cryptlib.h>
#include <cryptopp/config_int.h>
#include <cryptopp/secblock.h>
#include <cryptopp/smartptr.h>
#include <cryptopp/osrng.h>
#include <cryptopp/aes.h>
#include <cryptopp/sha.h>
#include <grpcpp/grpcpp.h>
#include "test.grpc.pb.h"
#include "test.pb.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "shares.grpc.pb.h"
#include "shares.pb.h"
#include <thread>

using web::http::http_request;
using web::http::http_response;
using web::http::methods;
using web::http::status_codes;
using web::http::experimental::listener::http_listener;
using web::http::experimental::listener::http_listener_config;
using web::uri_builder;

using grpc::Channel;
using grpc::ClientContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloRequest;
using helloworld::HelloResponse;

class GreeterServiceImpl final : public Greeter::Service {
  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloResponse* reply) override {
    std::string prefix("Hello ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
};

std::string read_keycert(const std::string& filepath) {
    std::ifstream file(filepath);
    
    if (!file.is_open()) {
        std::cerr << "Error opening file: " << filepath << std::endl;
        return "";
    }

    std::stringstream buffer;
    buffer << file.rdbuf();

    return buffer.str();
}

void RunGrpcServer() {
    std::string server_address("localhost:50052");
    GreeterServiceImpl service;
    ServerBuilder builder;

    grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp;
    pkcp.private_key = read_keycert("test-private-key.pem");
    pkcp.cert_chain = read_keycert("test-certificate.pem");
    grpc::SslServerCredentialsOptions ssl_opts;
    ssl_opts.pem_root_certs="";
    ssl_opts.pem_key_cert_pairs.push_back(pkcp);

    auto channel_creds = grpc::SslServerCredentials(ssl_opts);
    builder.AddListeningPort(server_address, channel_creds);
    builder.RegisterService(&service);

    std::unique_ptr<Server> server(builder.BuildAndStart());

    if (server) {
        std::cout << "gRPC Server listening on " << server_address << std::endl;
        server->Wait(); // Wait for the server to shut down
    } else {
        std::cerr << "Failed to start gRPC server" << std::endl;
    }
}

void RunRestServer() {
    utility::string_t address = U("https://localhost:8080");
    uri_builder uri(address);
    auto addr = uri.to_uri().to_string();
    
    http_listener_config config;
    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_sslv3
            | boost::asio::ssl::context::tlsv12);
        ctx.use_certificate_chain_file("test-certificate.crt");
        ctx.use_private_key_file("test-private-key.key", boost::asio::ssl::context::pem);
    });

    http_listener listener(addr, config);
    
    listener.support(methods::GET, [](http_request request) {
        request.reply(status_codes::OK, "Hello from secure REST server!");
    });

    try {
        listener.open().then([]() {
            std::cout << "REST Server started" << std::endl;
        }).wait();

        std::cout << "Listening for requests at: " << addr << std::endl;
        std::thread([=]() {
            while (true);
        }).detach();
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

int main() {
    std::thread grpc_server_thread(RunGrpcServer);
    std::thread rest_server_thread(RunRestServer);

    grpc_server_thread.join();
    rest_server_thread.join();

    return 0;
}

Building the above code using CMake

CMakeList also includes code to generate certificates:

find_package(cpprestsdk REQUIRED)
find_package(Boost REQUIRED program_options)
find_package(Catch2 CONFIG REQUIRED)
find_package(cryptopp CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(OpenSSL REQUIRED)
# generate keys and certificate for grpc with https unit tests
add_custom_target(
    keys ALL
    COMMAND openssl ecparam -genkey -name prime256v1 -noout -out test-private-key.pem
    COMMAND openssl ec -in test-private-key.pem -pubout -out test-public-key.pem
    COMMAND openssl req -new -x509 -sha256 -key test-private-key.pem -subj /CN=localhost -out test-certificate.pem
    COMMENT "Generating keys and certificate for https unit tests"
    BYPRODUCTS test-private-key.pem test-public-key.pem test-certificate.pem
)
# Proto file
get_filename_component(test_proto "protos/test.proto" ABSOLUTE)
get_filename_component(test_proto_path "${test_proto}" PATH)

set(VCPKG_TOOL_PATH "/opt/vcpkg/installed/x64-linux/tools")
# Generated files will be in the corresponding build directory
set(proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/test.pb.cc")
set(proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/test.pb.h")
set(grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/test.grpc.pb.cc")
set(grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/test.grpc.pb.h")

# Generating protobuf files for hello-world testing
add_custom_command(
    OUTPUT "${proto_srcs}" "${proto_hdrs}"
    COMMAND ${VCPKG_TOOL_PATH}/protobuf/protoc
    ARGS --cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
         -I "${test_proto_path}"
         "${test_proto}"
    DEPENDS "${test_proto}"
)
# Generating gRPC files
add_custom_command(
    OUTPUT "${grpc_srcs}" "${grpc_hdrs}"
    COMMAND ${VCPKG_TOOL_PATH}/protobuf/protoc
    ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
         --plugin=protoc-gen-grpc="${VCPKG_TOOL_PATH}/grpc/grpc_cpp_plugin"
         -I "${test_proto_path}"
         "${test_proto}"
    DEPENDS "${proto_srcs}"
)

include_directories(${CMAKE_CURRENT_BINARY_DIR})  # Add the directory containing generated files
include_directories(/opt/vcpkg/installed/x64-linux/include/grpc++ 
                    /opt/vcpkg/installed/x64-linux/include/grpcpp 
                    /opt/vcpkg/installed/x64-linux/include/grpc )

add_executable(grpc_test test/grpc.cpp ${proto_hdrs} ${proto_srcs} ${grpc_srcs} ${grpc_hdrs})
target_link_libraries(grpc_test PRIVATE protobuf::libprotobuf gRPC::grpc++ gRPC::grpc++_reflection
ssl ${Boost_LIBRARIES} crypto cryptopp::cryptopp yaml-cpp stdc++fs pthread gmp cpprest)

Now inside your project build directory you can run:

./grpc_test

This should replicate the above given error!

PS: All of these certificates are available in the correct location and should have been found by the servers!

How can I troubleshoot and resolve this problem to enable my servers to handle HTTPS requests effectively?

NOTE: I have tried running the above files with OpenSSL 1.1.1n and it doesn't give me any error and everything works. I fail to understand this behavior.

Any insights or suggestions would be greatly appreciated. Thank you!

0

There are 0 best solutions below