From 3a0802d130ca083d985f048ee1c7e540a69f8462 Mon Sep 17 00:00:00 2001 From: Ben Crowhurst Date: Thu, 31 Dec 2015 17:19:07 +1300 Subject: [PATCH] HTTPS client with peer verification; resolved #41. --- .gitattributes | 3 +- example/CMakeLists.txt | 8 +++ .../resource/certificates/578d5c04.0 | 1 + .../Equifax_Secure_Certificate_Authority.pem | 19 +++++ example/https_client/source/verify_peer.cpp | 72 +++++++++++++++++++ source/corvusoft/restbed/uri.hpp | 2 + 6 files changed, 104 insertions(+), 1 deletion(-) create mode 120000 example/https_client/resource/certificates/578d5c04.0 create mode 100644 example/https_client/resource/certificates/Equifax_Secure_Certificate_Authority.pem create mode 100644 example/https_client/source/verify_peer.cpp diff --git a/.gitattributes b/.gitattributes index e9dba388..98b4ae60 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20,8 +20,9 @@ CMakeLists.txt text *.bat eol=crlf # Denote all files that are truly binary and should not be modified. +*.pem binary *.a binary *.so binary *.dll binary *.dylib binary -*.lib binary \ No newline at end of file +*.lib binary diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 3381839b..108db213 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -103,12 +103,20 @@ if ( BUILD_SSL ) add_executable( https_client_verify_none https_client/source/verify_none.cpp ) target_link_libraries( https_client_verify_none ${CMAKE_PROJECT_NAME} ) + + add_executable( https_client_verify_peer https_client/source/verify_peer.cpp ) + target_link_libraries( https_client_verify_peer ${CMAKE_PROJECT_NAME} ) endif ( ) # # Install # install( DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example" DESTINATION ${CMAKE_INSTALL_PREFIX} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE ) + +install( DIRECTORY "${CMAKE_SOURCE_DIR}/example/https_client/resource/certificates" DESTINATION "${CMAKE_INSTALL_PREFIX}/resource" FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE ) + install( FILES "${CMAKE_SOURCE_DIR}/example/serving_html/resource/index.html" DESTINATION "resource" ) install( FILES "${CMAKE_SOURCE_DIR}/example/compression/resource/data.zlib" DESTINATION "resource" ) install( FILES "${CMAKE_SOURCE_DIR}/example/transfer_encoding_request/resource/request.txt" DESTINATION "resource" ) + + diff --git a/example/https_client/resource/certificates/578d5c04.0 b/example/https_client/resource/certificates/578d5c04.0 new file mode 120000 index 00000000..1ea571da --- /dev/null +++ b/example/https_client/resource/certificates/578d5c04.0 @@ -0,0 +1 @@ +Equifax_Secure_Certificate_Authority.pem \ No newline at end of file diff --git a/example/https_client/resource/certificates/Equifax_Secure_Certificate_Authority.pem b/example/https_client/resource/certificates/Equifax_Secure_Certificate_Authority.pem new file mode 100644 index 00000000..b5dd02fc --- /dev/null +++ b/example/https_client/resource/certificates/Equifax_Secure_Certificate_Authority.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/example/https_client/source/verify_peer.cpp b/example/https_client/source/verify_peer.cpp new file mode 100644 index 00000000..168b67ea --- /dev/null +++ b/example/https_client/source/verify_peer.cpp @@ -0,0 +1,72 @@ +/* + * Example illustrating a HTTPS client with peer verification. + * + * Get CA certifiate (http://gagravarr.org/writing/openssl-certs/general.shtml) + * note the subject hash never seems to match?!? use dtruss/strace to see what it wants to load. Its also more reliable + * to download the certificate straight from the CA (for google.com its https://www.geotrust.com/resources/root-certificates/) + * + * http://stackoverflow.com/questions/23343910/verify-errornum-20-when-connecting-to-gateway-sandbox-push-apple-com + * 1. openssl s_client -connect www.google.com:443 -showcerts + * 2. download root CA + * 3. test root CA, openssl s_client -connect www.google.com:443 -showcerts -CAfile ./certs/Equifax_Secure_Certificate_Authority.pem + * Each file in the directory must contain a single certificate. + * The files must be named using the subject name's hash and an extension of ".0". + * 4. openssl x509 -in certs/Equifax_Secure_Certificate_Authority.pem -noout -subject_hash (also see c_rehash) + * can be used to generate the subject name hash(e.g. e5d93f80). Just append ".0" to this value and create a softlink with this name(e5d93f80.0) to the CA file. If there are more than one CA files with same the subject name hash value, their extensions should be different(e.g. e5d93f80.1). The search is performed in the ordering of the extension number. + * opensssl has introduced this technique to reduce the the CA file look up time. Otherwise openssl may have to read all files in the CApath to find the matching CA file. + * 5. ln -s Equifax_Secure_Certificate_Authority.pem 594f1775.0 + * 6. verify its all setup correct, openssl s_client -showcerts -CApath ./certs -connect google.com:443 + * 7. make sure the file permissions are correct for the CA root cert (http://stackoverflow.com/questions/15936668/boostasiosslcontextadd-verify-path) + * + * to confirm loading of the right hash use strace or dtruss (on mac) to verifiy the hash file being loaded. + * Usage: + * ./distribution/example/https_client_verify_peer + */ + +#include +#include +#include +#include + +using namespace std; +using namespace restbed; + +int main( const int, const char** ) +{ + auto request = make_shared< Request >( Uri( "https://www.google.com" ) ); + request->set_header( "Accept", "*/*" ); + request->set_header( "Host", "www.google.com" ); + request->set_query_parameter( "query", "search term" ); + + auto settings = make_shared< SSLSettings >( ); + settings->set_certificate_authority_pool( Uri( "file://distribution/resource/certificates", Uri::Relative ) ); + + auto response = Http::sync( request, settings ); + + fprintf( stderr, "*** Response ***\n" ); + fprintf( stderr, "Status Code: %i\n", response->get_status_code( ) ); + fprintf( stderr, "Status Message: %s\n", response->get_status_message( ).data( ) ); + fprintf( stderr, "HTTP Version: %.1f\n", response->get_version( ) ); + fprintf( stderr, "HTTP Protocol: %s\n", response->get_protocol( ).data( ) ); + + for ( const auto header : response->get_headers( ) ) + { + fprintf( stderr, "Header '%s' > '%s'\n", header.first.data( ), header.second.data( ) ); + } + + if ( response->has_header( "Transfer-Encoding" ) ) + { + Http::fetch( "\r\n", response ); + } + else + { + auto length = 0; + response->get_header( "Content-Length", length ); + + Http::fetch( length, response ); + } + + fprintf( stderr, "Body: %.*s...\n", 3, response->get_body( ).data( ) ); + + return EXIT_SUCCESS; +} diff --git a/source/corvusoft/restbed/uri.hpp b/source/corvusoft/restbed/uri.hpp index 015ed046..73f7a09c 100644 --- a/source/corvusoft/restbed/uri.hpp +++ b/source/corvusoft/restbed/uri.hpp @@ -35,6 +35,8 @@ namespace restbed //Friends //Definitions + static const bool Relative = true; + static const bool Absolute = false; //Constructors explicit Uri( const std::string& value, bool relative = false );