Use TLS client authentication with casablanca / cpprest in Linux - c++

I wanted to switch over to cpprest for REST requirements, however, in my scenario, TLS client authentication needs to be supported.
I went through the capabilities of the library, but I am not sure whether there is built in support for client authentication.
There are some hacks for trying to get this capability, but it seems only on Windows for the moment, which won't work for me, since my server is on Linux.
This page here, https://github.com/Microsoft/cpprestsdk/issues/810, describes one such get-around for Windows, and a hint of how it can work in Linux builds, but I am not sure how to go about it, since I have little experience in the area.
Work around for TLS client authentication on Windows -
#include <Wincrypt.h>
std::vector<uint8_t> pkcs12_data; // "... your client certificate PKCS#12 with private key goes here ...";
utility::string_t password = "pkcs12_password";
web::http::client::http_client_config cfg;
cfg.set_nativehandle_options([=] (web::http::client::native_handle h) {
CRYPT_DATA_BLOB data;
data.cbData = pkcs12_data.size();
data.pbData = reinterpret_cast<BYTE *>(pkcs12_data.data());
HCERTSTORE hCertStore = PFXImportCertStore(&data, password.c_str(), 0);
PCCERT_CONTEXT hContext = CertFindCertificateInStore(
hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY,
NULL, NULL);
WinHttpSetOption(h, WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
(LPVOID)hContext, sizeof(CERT_CONTEXT));
});
web::http::client::http_client http_client(PS("https://secure.com"), cfg);

Related

I'm trying to use the LDAP extended operation start tls using c++. But after executing the start tls extension i'm unable to make the simple bind

This is the part of the code i'm working with to establish the start tls extended operation. The binding is not working after the execution of start tls.
BerElement *ber;//extended
struct berval *bv;
char *retoid;
struct berval *retdata;
struct berval oid;
oid.bv_val=(char*)LDAP_EXOP_START_TLS;
oid.bv_len=strlen(oid.bv_val);
ber=ber_alloc_t(LBER_USE_DER);
cout<<"ber: "<<ber<<"\n";
ber_printf(ber,"{");
ber_printf(ber,"tO",LDAP_EXOP_START_TLS,oid);
ber_printf(ber,"N}");
res=ber_flatten(ber,&bv);
ber_free(ber,1);
cout<<"berf: "<<res<<"\n";
res=ldap_extended_operation_s(ldap, LDAP_EXOP_START_TLS, bv, NULL, NULL, &retoid, &retdata);
cout<<res<<"\n";
res = ldap_simple_bind_s(ldap, user_dn,pass);//binds
cout<<"bind: "<<res<<"\n";
Instead of ldap_extended_operation_s() function, just use ldap_start_tls_s().
ldap_start_tls_s() automatically sends request for checking StartTLS option on server before starting any communication.
See details on man page

WinHttpSetOption() failed set TLSv1.2 with error code ERROR_INTERNET_INCORRECT_HANDLE_TYPE

I am trying to set TLSv1.1 or v1.2 from C++ (Win) code using cpprest API calls as mentioned. But WinHttpSetOption() is failing with error ERROR_INTERNET_INCORRECT_HANDLE_TYPE (12018).
OS:Windows(7/8)
Tried to set TLSv1.1 and TLS1.2 from registry setting did not work.
Tried to get OpenSLL but opensll1.0.1(which supports TLS1.1 and more) is not available for windows.
Tried to get other than native handle did not find API
auto func = [&](web::http::client::native_handle handle){
BOOL win32Result{ FALSE };
DWORD secure_protocols{ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1
| WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 };
win32Result = ::WinHttpSetOption(handle,
WINHTTP_OPTION_SECURE_PROTOCOLS,
&secure_protocols,
sizeof(secure_protocols));
if (FALSE == win32Result) {
std::cout << "Can not set TLS 1.1 or TLS 1.2." << std::endl;
auto err = GetLastError();
CString cstr;
cstr.Format(_T("err = %d"),err);
AfxMessageBox(cstr);
}
};
config.set_validate_certificates(false);
config.set_nativehandle_options(func);
Please help me to set TLSv1.1 or v1.2 using C++ REST API. Or
how to make WinHttpSetOption() successful.
Using WinHttpOpen we can get "session handle" which can be passed to WinHttpSetOption().
This resolve the error "ERROR_INTERNET_INCORRECT_HANDLE_TYPE ".
HINTERNET hSession = WinHttpOpen(L"<Application name>",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
Though i am setting the TLS version to 1.2/1.1. Still my "http_request" is using TLSv1.0 which is default in Windows 7/8.1.(This is can confirm using wireshark)
Can any one let me know why "http_request" still using TLS1.0.
Please try installing this update:
https://support.microsoft.com/en-gb/topic/update-to-enable-tls-1-1-and-tls-1-2-as-default-secure-protocols-in-winhttp-in-windows-c4bd73d2-31d7-761e-0178-11268bb10392
"This update provides support for Transport Layer Security (TLS) 1.1 and TLS 1.2 in Windows Server 2012, Windows 7 Service Pack 1 (SP1), and Windows Server 2008 R2 SP1."

Native SSL Support for WINAPI

I'm trying to make a simple IRC client, using the Winsock API, to which I want to add SSL support. Currently I just use overlapped socket I/O like this:
SOCKET sock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0x02, 0x01);
if (!sock)
return;
struct sockaddr_in ircClient;
memcpy(&ircClient.sin_addr, he->h_addr_list[0], he->h_length);
ircClient.sin_family = AF_INET;
ircClient.sin_port = wPort;
WSAEVENT hDataEvent = WSA_INVALID_EVENT;
if (WSAConnect(sock, (sockaddr*)&ircClient, sizeof(ircClient), 0, 0, 0, 0) > 0) {
closesocket(sock);
return;
}
if (wsWSAGetLastError() != 0) {
closesocket(sock);
return;
}
Now, as I understand, for SSL support, I need to do SSL handshake after WSAConnect(). I found old Internet posts saying there are no SSL support in Winsock. It is now is year 2017, and 95% of websites work with SSL. Is there still no way to do this? I have found Using Secure Socket Extensions, but it is not SSL.
I've done years ago some SSL/TLS stuff over standard TCP connections using native windows API, but I'm not familiar with this specific "secure socket extension".
I can recommend using SSPI. It doesn't automatically transform your socket to SSL, but can be used pretty easy for generating SSL request/response/data packets on request.
Look for InitializeSecurityContext for more info.

Reliable way to get root CA certificates on Windows

I'm using boost asio to connect to my valid certificate (signed by root CA). The code I'm using is the ssl client example available from boost docs.
The only line I added is:
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23_client);
ctx.set_default_verify_paths(); <------------- Add default verification paths
ctx.set_password_callback(&password_callback);
client c(io_service, ctx, iterator);
io_service.run();
the problem is: when using this code with a locally installed copy of openSSH (installed from a msi installer) the paths are found correctly and my certificate validated. When I download my own copy of the openSSH repository and compile it this line no longer works and I don't have root CA certificates to validate my own one (therefore it fails).
Since I'd like to eventually distribute these clients on customer machines I'd like to avoid setting environment variables like SSL_CERT_DIR and the like. How can I find root CA certificates with boost asio reliably or alternatively configure my openSSH from source compilation to find them?
You can load the root CAs from the windows CA store. It already contains the "default" trusted root CA certificates and can be managed through certmgr. Use the following function to replace set_default_verify_paths under windows:
#include <boost/asio/ssl/context.hpp>
#include <wincrypt.h>
void add_windows_root_certs(boost::asio::ssl::context &ctx)
{
HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");
if (hStore == NULL) {
return;
}
X509_STORE *store = X509_STORE_new();
PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) {
X509 *x509 = d2i_X509(NULL,
(const unsigned char **)&pContext->pbCertEncoded,
pContext->cbCertEncoded);
if(x509 != NULL) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
SSL_CTX_set_cert_store(ctx.native_handle(), store);
}
This will load the certificates from the windows ca store. It uses d2i_X509 to convert them to the internal OpenSSL format and adds them to an OpenSSL X509_STORE. Then SSL_CTX_set_cert_store attaches that store to the boost ssl context. You can use that to set up your ssl context:
namespace ssl = boost::asio::ssl;
ssl::context ctx(ssl::context::tlsv12_client);
ctx.set_options(ssl::context::default_workarounds
| ssl::context::no_sslv2
| ssl::context::no_sslv3
| ssl::context::tlsv12_client);
#if BOOST_OS_WINDOWS
add_windows_root_certs(ctx);
#else
ctx.set_default_verify_paths();
#endif
ctx.set_password_callback(&password_callback);
client c(io_service, ctx, iterator);
io_service.run();
Note: You will probably need to add crypt32 to your linked libraries.
Note 2: BOOST_OS_WINDOWS needs Boost Predef

Monitoring Application Network Traffic in Qt

I've been searching for this for a while, but is there a way to monitor the network traffic coming/going from a specific application using the appliances that are already built into the Qt library? I've found some solutions using things like WinPCap and other similar utilities, but I was hoping to keep it all in the Qt (in C++) house as that is what I am most familiar with.
Thanks! Any pointers are greatly appreciated!
Some example of c# sniffer socket creation.
mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.IP);
// Bind the socket to the selected IP address
mainSocket.Bind(newIPEndPoint(IPAddress.Parse(cmbInterfaces.Text),0));
// Set the socket options
mainSocket.SetSocketOption(SocketOptionLevel.IP, //Applies only to IP packets
SocketOptionName.HeaderIncluded, //Set the include header
true); //option to true
byte[] byTrue = newbyte[4]{1, 0, 0, 0};
byte[] byOut = newbyte[4];
//Socket.IOControl is analogous to the WSAIoctl method of Winsock 2
mainSocket.IOControl(IOControlCode.ReceiveAll, //SIO_RCVALL of Winsock
byTrue, byOut);
//Start receiving the packets asynchronously
mainSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None,
newAsyncCallback(OnReceive), null);
May be qt provides some api for creating such sockets.After creating such socket you'll get raw pacets on it parsing headers and other info is on you.