Hello world example of a TLS client: fetch an HTTPS page. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-tls
HelloHttpsClient.cpp
00001 /* 00002 * Hello world example of a TLS client: fetch an HTTPS page 00003 * 00004 * Copyright (C) 2006-2018, Arm Limited, All Rights Reserved 00005 * SPDX-License-Identifier: Apache-2.0 00006 * 00007 * Licensed under the Apache License, Version 2.0 (the "License"); you may 00008 * not use this file except in compliance with the License. 00009 * You may obtain a copy of the License at 00010 * 00011 * http://www.apache.org/licenses/LICENSE-2.0 00012 * 00013 * Unless required by applicable law or agreed to in writing, software 00014 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 00015 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00016 * See the License for the specific language governing permissions and 00017 * limitations under the License. 00018 * 00019 * This file is part of Mbed TLS (https://tls.mbed.org) 00020 */ 00021 00022 #include "HelloHttpsClient.h" 00023 00024 #include "mbedtls/platform.h" 00025 #include "mbedtls/config.h" 00026 #include "mbedtls/ssl.h" 00027 #include "mbedtls/entropy.h" 00028 #include "mbedtls/ctr_drbg.h" 00029 #include "mbedtls/error.h" 00030 #include "mbedtls/debug.h" 00031 #include "mbedtls/x509.h" 00032 00033 #include <stdint.h> 00034 #include <string.h> 00035 #include "mbed.h" 00036 00037 const char *HelloHttpsClient::DRBG_PERSONALIZED_STR = 00038 "Mbed TLS helloword client"; 00039 00040 const size_t HelloHttpsClient::ERROR_LOG_BUFFER_LENGTH = 128; 00041 00042 const char *HelloHttpsClient::TLS_PEM_CA = 00043 "-----BEGIN CERTIFICATE-----\n" 00044 "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" 00045 "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" 00046 "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" 00047 "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" 00048 "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" 00049 "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" 00050 "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" 00051 "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" 00052 "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" 00053 "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" 00054 "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" 00055 "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" 00056 "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" 00057 "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" 00058 "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" 00059 "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" 00060 "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" 00061 "rqXRfboQnoZsG4q5WTP468SQvvG5\n" 00062 "-----END CERTIFICATE-----\n"; 00063 00064 const char *HelloHttpsClient::HTTP_REQUEST_FILE_PATH = 00065 "/media/uploads/mbed_official/hello.txt"; 00066 00067 const char *HelloHttpsClient::HTTP_HELLO_STR = "Hello world!"; 00068 00069 const char *HelloHttpsClient::HTTP_OK_STR = "200 OK"; 00070 00071 HelloHttpsClient::HelloHttpsClient(const char *in_server_name, 00072 const char *in_server_addr, 00073 const uint16_t in_server_port) : 00074 socket(), 00075 server_name(in_server_name), 00076 server_addr(in_server_addr), 00077 server_port(in_server_port) 00078 { 00079 mbedtls_entropy_init(&entropy); 00080 mbedtls_ctr_drbg_init(&ctr_drbg); 00081 mbedtls_x509_crt_init(&cacert); 00082 mbedtls_ssl_init(&ssl); 00083 mbedtls_ssl_config_init(&ssl_conf); 00084 } 00085 00086 HelloHttpsClient::~HelloHttpsClient() 00087 { 00088 mbedtls_entropy_free(&entropy); 00089 mbedtls_ctr_drbg_free(&ctr_drbg); 00090 mbedtls_x509_crt_free(&cacert); 00091 mbedtls_ssl_free(&ssl); 00092 mbedtls_ssl_config_free(&ssl_conf); 00093 00094 socket.close(); 00095 } 00096 00097 int HelloHttpsClient::run() 00098 { 00099 int ret; 00100 size_t req_len, req_offset, resp_offset; 00101 uint32_t flags; 00102 bool resp_200, resp_hello; 00103 00104 /* Configure the TCPSocket */ 00105 if ((ret = configureTCPSocket()) != 0) 00106 return ret; 00107 00108 /* Configure already initialized Mbed TLS structures */ 00109 if ((ret = configureTlsContexts()) != 0) 00110 return ret; 00111 00112 /* Start a connection to the server */ 00113 if ((ret = socket.connect(server_addr, server_port)) != NSAPI_ERROR_OK) { 00114 mbedtls_printf("socket.connect() returned %d\n", ret); 00115 return ret; 00116 } 00117 mbedtls_printf("Successfully connected to %s at port %u\n", 00118 server_addr, server_port); 00119 00120 /* Start the TLS handshake */ 00121 mbedtls_printf("Starting the TLS handshake...\n"); 00122 do { 00123 ret = mbedtls_ssl_handshake(&ssl); 00124 } while(ret != 0 && 00125 (ret == MBEDTLS_ERR_SSL_WANT_READ || 00126 ret == MBEDTLS_ERR_SSL_WANT_WRITE)); 00127 if (ret < 0) { 00128 mbedtls_printf("mbedtls_ssl_handshake() returned -0x%04X\n", -ret); 00129 return ret; 00130 } 00131 mbedtls_printf("Successfully completed the TLS handshake\n"); 00132 00133 /* Fill the request buffer */ 00134 ret = snprintf(gp_buf, sizeof(gp_buf), 00135 "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", HTTP_REQUEST_FILE_PATH, 00136 server_name); 00137 req_len = static_cast<size_t>(ret); 00138 if (ret < 0 || req_len >= sizeof(gp_buf)) { 00139 mbedtls_printf("Failed to compose HTTP request using snprintf: %d\n", 00140 ret); 00141 return ret; 00142 } 00143 00144 /* Send the HTTP request to the server over TLS */ 00145 req_offset = 0; 00146 do { 00147 ret = mbedtls_ssl_write(&ssl, 00148 reinterpret_cast<const unsigned char *>(gp_buf + req_offset), 00149 req_len - req_offset); 00150 if (ret > 0) 00151 req_offset += static_cast<size_t>(ret); 00152 } 00153 while(req_offset < req_len && 00154 (ret > 0 || 00155 ret == MBEDTLS_ERR_SSL_WANT_WRITE || 00156 ret == MBEDTLS_ERR_SSL_WANT_READ)); 00157 if (ret < 0) { 00158 mbedtls_printf("mbedtls_ssl_write() returned -0x%04X\n", -ret); 00159 return ret; 00160 } 00161 00162 /* Print information about the TLS connection */ 00163 ret = mbedtls_x509_crt_info(gp_buf, sizeof(gp_buf), 00164 "\r ", mbedtls_ssl_get_peer_cert(&ssl)); 00165 if (ret < 0) { 00166 mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret); 00167 return ret; 00168 } 00169 mbedtls_printf("Server certificate:\n%s\n", gp_buf); 00170 00171 /* Ensure certificate verification was successful */ 00172 flags = mbedtls_ssl_get_verify_result(&ssl); 00173 if (flags != 0) { 00174 ret = mbedtls_x509_crt_verify_info(gp_buf, sizeof(gp_buf), 00175 "\r ! ", flags); 00176 if (ret < 0) { 00177 mbedtls_printf("mbedtls_x509_crt_verify_info() returned " 00178 "-0x%04X\n", -ret); 00179 return ret; 00180 } else { 00181 mbedtls_printf("Certificate verification failed (flags %lu):" 00182 "\n%s\n", flags, gp_buf); 00183 return -1; 00184 } 00185 } else { 00186 mbedtls_printf("Certificate verification passed\n"); 00187 } 00188 00189 mbedtls_printf("Established TLS connection to %s\n", server_name); 00190 00191 /* Read response from the server */ 00192 resp_offset = 0; 00193 resp_200 = false; 00194 resp_hello = false; 00195 do { 00196 ret = mbedtls_ssl_read(&ssl, 00197 reinterpret_cast<unsigned char *>(gp_buf + resp_offset), 00198 sizeof(gp_buf) - resp_offset - 1); 00199 if (ret > 0) 00200 resp_offset += static_cast<size_t>(ret); 00201 00202 /* Ensure that the response string is null-terminated */ 00203 gp_buf[resp_offset] = '\0'; 00204 00205 /* Check if we received expected string */ 00206 resp_200 = resp_200 || strstr(gp_buf, HTTP_OK_STR) != NULL; 00207 resp_hello = resp_hello || strstr(gp_buf, HTTP_HELLO_STR) != NULL; 00208 } while((!resp_200 || !resp_hello) && 00209 (ret > 0 || 00210 ret == MBEDTLS_ERR_SSL_WANT_READ || MBEDTLS_ERR_SSL_WANT_WRITE)); 00211 if (ret < 0) { 00212 mbedtls_printf("mbedtls_ssl_read() returned -0x%04X\n", -ret); 00213 return ret; 00214 } 00215 00216 /* Display response information */ 00217 mbedtls_printf("HTTP: Received %u chars from server\n", resp_offset); 00218 mbedtls_printf("HTTP: Received '%s' status ... %s\n", HTTP_OK_STR, 00219 resp_200 ? "OK" : "FAIL"); 00220 mbedtls_printf("HTTP: Received message:\n%s\n", gp_buf); 00221 00222 return 0; 00223 } 00224 00225 int HelloHttpsClient::configureTCPSocket() 00226 { 00227 int ret; 00228 00229 NetworkInterface *network = NetworkInterface::get_default_instance(); 00230 if(network == NULL) { 00231 mbedtls_printf("ERROR: No network interface found!\n"); 00232 return -1; 00233 } 00234 ret = network->connect(); 00235 if (ret != 0) { 00236 mbedtls_printf("Error! network->connect() returned: %d\n", ret); 00237 return ret; 00238 } 00239 00240 if ((ret = socket.open(network)) != NSAPI_ERROR_OK) { 00241 mbedtls_printf("socket.open() returned %d\n", ret); 00242 return ret; 00243 } 00244 00245 socket.set_blocking(false); 00246 00247 return 0; 00248 } 00249 00250 int HelloHttpsClient::configureTlsContexts() 00251 { 00252 int ret; 00253 00254 ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, 00255 reinterpret_cast<const unsigned char *>(DRBG_PERSONALIZED_STR), 00256 strlen(DRBG_PERSONALIZED_STR) + 1); 00257 if (ret != 0) { 00258 mbedtls_printf("mbedtls_ctr_drbg_seed() returned -0x%04X\n", -ret); 00259 return ret; 00260 } 00261 00262 ret = mbedtls_x509_crt_parse(&cacert, 00263 reinterpret_cast<const unsigned char *>(TLS_PEM_CA), 00264 strlen(TLS_PEM_CA) + 1); 00265 if (ret != 0) { 00266 mbedtls_printf("mbedtls_x509_crt_parse() returned -0x%04X\n", -ret); 00267 return ret; 00268 } 00269 00270 ret = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_CLIENT, 00271 MBEDTLS_SSL_TRANSPORT_STREAM, 00272 MBEDTLS_SSL_PRESET_DEFAULT); 00273 if (ret != 0) { 00274 mbedtls_printf("mbedtls_ssl_config_defaults() returned -0x%04X\n", 00275 -ret); 00276 return ret; 00277 } 00278 00279 mbedtls_ssl_conf_ca_chain(&ssl_conf, &cacert, NULL); 00280 mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &ctr_drbg); 00281 00282 /* 00283 * It is possible to disable authentication by passing 00284 * MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode() 00285 */ 00286 mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); 00287 00288 /* Configure certificate verification function to clear time/date flags */ 00289 mbedtls_ssl_conf_verify(&ssl_conf, sslVerify, this); 00290 00291 #if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 00292 mbedtls_ssl_conf_dbg(&ssl_conf, sslDebug, NULL); 00293 mbedtls_debug_set_threshold(HELLO_HTTPS_CLIENT_DEBUG_LEVEL); 00294 #endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */ 00295 00296 if ((ret = mbedtls_ssl_setup( &ssl, &ssl_conf)) != 0) { 00297 mbedtls_printf("mbedtls_ssl_setup() returned -0x%04X\n", -ret); 00298 return ret; 00299 } 00300 00301 if ((ret = mbedtls_ssl_set_hostname( &ssl, server_name )) != 0) { 00302 mbedtls_printf("mbedtls_ssl_set_hostname() returned -0x%04X\n", 00303 -ret); 00304 return ret; 00305 } 00306 00307 mbedtls_ssl_set_bio(&ssl, static_cast<void *>(&socket), sslSend, sslRecv, 00308 NULL); 00309 00310 return 0; 00311 } 00312 00313 int HelloHttpsClient::sslRecv(void *ctx, unsigned char *buf, size_t len) 00314 { 00315 TCPSocket *socket = static_cast<TCPSocket *>(ctx); 00316 int ret = socket->recv(buf, len); 00317 00318 if (ret == NSAPI_ERROR_WOULD_BLOCK) 00319 ret = MBEDTLS_ERR_SSL_WANT_READ; 00320 else if (ret < 0) 00321 mbedtls_printf("socket.recv() returned %d\n", ret); 00322 00323 return ret; 00324 } 00325 00326 int HelloHttpsClient::sslSend(void *ctx, const unsigned char *buf, size_t len) 00327 { 00328 TCPSocket *socket = static_cast<TCPSocket *>(ctx); 00329 int ret = socket->send(buf, len); 00330 00331 if (ret == NSAPI_ERROR_WOULD_BLOCK) 00332 ret = MBEDTLS_ERR_SSL_WANT_WRITE; 00333 else if (ret < 0) 00334 mbedtls_printf("socket.send() returned %d\n", ret); 00335 00336 return ret; 00337 } 00338 00339 void HelloHttpsClient::sslDebug(void *ctx, int level, const char *file, 00340 int line, const char *str) 00341 { 00342 (void)ctx; 00343 00344 const char *p, *basename; 00345 00346 /* Extract basename from file */ 00347 for (p = basename = file; *p != '\0'; p++) { 00348 if (*p == '/' || *p == '\\') 00349 basename = p + 1; 00350 } 00351 00352 mbedtls_printf("%s:%d: |%d| %s\r", basename, line, level, str); 00353 } 00354 00355 int HelloHttpsClient::sslVerify(void *ctx, mbedtls_x509_crt *crt, int depth, 00356 uint32_t *flags) 00357 { 00358 int ret = 0; 00359 00360 /* 00361 * If MBEDTLS_HAVE_TIME_DATE is defined, then the certificate date and time 00362 * validity checks will probably fail because this application does not set 00363 * up the clock correctly. We filter out date and time related failures 00364 * instead 00365 */ 00366 *flags &= ~MBEDTLS_X509_BADCERT_FUTURE & ~MBEDTLS_X509_BADCERT_EXPIRED; 00367 00368 #if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 00369 HelloHttpsClient *client = static_cast<HelloHttpsClient *>(ctx); 00370 00371 ret = mbedtls_x509_crt_info(client->gp_buf, sizeof(gp_buf), "\r ", crt); 00372 if (ret < 0) { 00373 mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret); 00374 } else { 00375 ret = 0; 00376 mbedtls_printf("Verifying certificate at depth %d:\n%s\n", 00377 depth, client->gp_buf); 00378 } 00379 #endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */ 00380 00381 return ret; 00382 }
Generated on Tue Jul 12 2022 19:35:00 by 1.7.2