microsoft / cpprestsdk

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
Other
7.97k stars 1.65k forks source link

https: certificate pinning #314

Open marnef opened 7 years ago

marnef commented 7 years ago

Hi all,

I'm trying to implement certificate pinning. This means that the server is sending the untrusted certificate [which is why I need to call http_client_config::set_validate_certificates(false) -> unsafe] and I have to compare its public key or a hash value of it with a value known at compile time. That's all under Windows, which means there is no set_ssl_context_callback.

Does anyone have an idea how I can install a callback that allows my check of the hash or any other idea? Or how can I access the certificate at all?

marnef commented 7 years ago

To make it clearer: I want to inject a method that checks the certificate during SSL handshake. If the method returns true the certificate is accepted.

marnef commented 7 years ago

Here is an example how easy that works in .net: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#.Net

I think a good solution would be an analog callback in the http_client_config class. It would be called in asio_connection:async_handshake.

deeringc commented 7 years ago

Have a look, it's been implemented in this fork: https://github.com/glukacsy/cpprestsdk

marnef commented 7 years ago

Thank you for that information. Good to see that there is progress on that topic. Although the discussion on https://github.com/Microsoft/cpprestsdk/pull/135 seems to have been stopped some time ago.

idotene commented 7 years ago

I'm looking forward for a solution for certificate pinning as well, for linux & OSX though.

marnef commented 7 years ago

My workaround is the following (under Windows, but probably you can adapt for other OS):

::HINTERNET hInternet = nullptr; http_client_config config; config.set_nativehandle_options([&](native_handle hNative) { if(hInternet == nullptr) hInternet = reinterpret_cast<::HINTERNET>(hNative); });

bool bCertTested = false; http_request request; rRequest.set_progress_handler([&](message_direction::direction eDirection, utility::size64_t uiProgress) { if(hInternet != nullptr && !bCertTested) { ::PCCERT_CONTEXT pCertificateEncoded; DWORD dwCertificateInfoSize = sizeof(pCertificateEncoded); if(::WinHttpQueryOption(hInternet, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &pCertificateEncoded, &dwCertificateInfoSize)) { const uint8_t *puiCertificate = reinterpret_cast<const uint8_t*>(pCertificateEncoded->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData); size_t uiCertificateSize = pCertificateEncoded->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData; ::CertFreeCertificateContext(pCertificateEncoded); ...

marnef commented 7 years ago

Not so nice about this solution: The content is downloaded before the certificate is checked. I consider a certificate error as just that and so for me that is ok.

idotene commented 7 years ago

Hi @marnef, I tried to implement this for linux, yet in my case the appropriate object is SSL (instead of HINTERNET), and it is never filled with the certificate data, not in the progress_handler, and not afterwards when I parse the actual response. I suspect that the SSL object is freed before calling to the progress handler. What I plan to do now is cloning the sources and try fixing the http_client_asio file so it will check the certificate according to the configuration. Currently it is checked with a predefined method and ignoring the injected method in the configuration. The method to change is 'handle_cert_verification' inside http_client_asio

idotene commented 7 years ago

Anyway as suggested above, in order to achieve pinning here is what I did:

In the file https://github.com/Microsoft/cpprestsdk/blob/master/Release/src/http/client/http_client_asio.cpp

override the method

bool handle_cert_verification(bool preverified, boost::asio::ssl::verify_context &verifyCtx) then recompile the library and replace it in your project.

The correct fix is to call the predefined set_verify_callback, which should be done in http_client_asio instead of calling a predefined validation function.

Tom883 commented 7 years ago

Hi @marnef , How did you end up solving this issue? I'm currently facing the same problem on windows .

marnef commented 7 years ago

Hi @Tom883, see my comment from June 15. It works fine, but is not really nice because it's OS dependent.

chogorma commented 7 years ago

Hi, We have updated how we do our cert-pinning: https://github.com/glukacsy/cpprestsdk

The design changed along with our requirements for accepting the certificate chain. What happens now, Casablanca calls back with the encoded data of the full certificate chain, where using OpenSSL the consumer will decide on whether to accept or reject the connection.

Using this approach we leave the validation of the certificate chain up to the consumer of casablanca.

matra774 commented 6 years ago

@chogorma, can you prepare pull requests? This seams like a really useful option.

chogorma commented 6 years ago

@matra774, yes i can do that, will get the PR open as soon as i can.

chogorma commented 6 years ago

Opened a PR for the approach we have taken: https://github.com/Microsoft/cpprestsdk/pull/702