ESP32学习之HTTPS 请求

ESP32学习之HTTPS 请求

​ 在ESP32的官方例程中是提供有关https request 例程,具体位置在esp-idf中examples下protocols\http_server\simple文件夹中,该例程简单演示了如何使用esp32作为http客户端请求服务器。

​ https request的整个过程是建立在tcp协议上的,主要通过访问服务器域名的形式来从服务器中获取数据。官方例程主要是通过get请求获取www.howsmyssl.com获取数据,具体流程如下:

void app_main(void)
{
    /*1. 硬件初始化*/
    /*初始化nvs存储*/
    ESP_ERROR_CHECK( nvs_flash_init() );
    /*网络初始化*/
    ESP_ERROR_CHECK(esp_netif_init());
    /*循环事件初始化*/
    ESP_ERROR_CHECK(esp_event_loop_create_default());

	/*2.连接wifi*/
    ESP_ERROR_CHECK(example_connect());
   
    /*3.创建https request 任务*/
    xTaskCreate(&https_request_task, "https_get_task", 8192, NULL, 5, NULL);
}

下面主要介绍下如何实现http_request_task

​ 在整个例程中以三种方式验证对等体的服务器证书或在预共享密钥的帮助下来验证服务器

static void https_request_task(void *pvparameters)
{
    ESP_LOGI(TAG, "Start https_request example");

    https_get_request_using_crt_bundle();
    https_get_request_using_cacert_buf();
    https_get_request_using_global_ca_store();

    ESP_LOGI(TAG, "Finish https_request example");
    vTaskDelete(NULL);
}

  1. https_get_request_using_crt_bundle主要使用cacert_buf和cacert_bytes,他们是CA证书可以在esp_tls_cfg_t结构的缓冲区中提供。ESP-TLS 将使用缓冲区中存在的 CA 证书来验证服务器。必须设置esp_tls_cfg_t结构中的以下变量。
cacert_buf- 指向包含 CA 证书的缓冲区的指针。

cacert_bytes- CA 证书的大小(以字节为单位)。

其具体实现:


static void https_get_request_using_cacert_buf(void)
{
    ESP_LOGI(TAG, "https_request using cacert_buf");
    /*
    	1.指定cacert_buf缓冲区中的pem二进制文件
    	2.cacert_bytes为该二进制文件大小
    */
    esp_tls_cfg_t cfg = {
        .cacert_buf = (const unsigned char *) server_root_cert_pem_start,
        .cacert_bytes = server_root_cert_pem_end - server_root_cert_pem_start,
    };
    https_get_request(cfg);
}

server_root_cert_pem文件是通过将其注册到组件中,具体使用方式:

您可以在组件注册中指定参数,为要嵌入的文件提供以空格分隔的名称:EMBED_FILES

idf_component_register(...
                       EMBED_FILES server_root_cert.der)

或者,如果文件是字符串,则可以使用变量 。这会将文本文件的内容作为空终止字符串嵌入:EMBED_TXTFILES

idf_component_register(...
                       EMBED_TXTFILES server_root_cert.pem)

该文件的内容将在 flash 中添加到 .rodata 部分,并通过符号名称提供,如下所示:

extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[]   asm("_binary_server_root_cert_pem_end");

这些名称是根据文件的全名生成的,如 中所示。字符 /、.等将替换为下划线。符号名称中的_binary前缀由 objcopy 添加,对于文本和二进制文件都是相同的。EMBED_FILES

要将文件嵌入到项目中,而不是将组件嵌入到项目中,可以按如下方式调用该函数:target_add_binary_data

target_add_binary_data(myproject.elf "main/data.bin" TEXT)

将此行放在项目 CMakeLists.txt 文件中的行之后。替换为您的项目名称。最后一个参数可以是嵌入以 null 结尾的字符串,也可以是按原样嵌入内容。project()``myproject.elf``TEXT``BINARY

  1. https_get_request_using_crt_bundle主要使用crt_bundle_attach方式,该方式是ESP x509 证书捆绑包 API 提供了一种简单的方法来包含用于 TLS 服务器验证的自定义 x509 根证书捆绑包。捆绑包主要过menuconfig配置完成的,然后通过cmake生成捆绑包并将其嵌入。

​ 在配置选项中有以下几项需要配置:

  • CONFIG_MBEDTLS_CERTIFICATE_BUNDLE:自动构建并附加捆绑包。
  • CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE:从完整的根列表中确定要包含的证书。
  • CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH:指定要嵌入到捆绑包中的任何其他证书的路径。

具体实现

static void https_get_request_using_crt_bundle(void)
{
    ESP_LOGI(TAG, "https_request using crt bundle");
    /*启动根证书捆绑包,并指定捆绑软件附加功能*/
    esp_tls_cfg_t cfg = {
        .crt_bundle_attach = esp_crt_bundle_attach,
    };
    https_get_request(cfg);
}
  1. https_get_request_using_global_ca_store主要使用use_global_ca_store,它只用一次初始化和设置。然后,它可用于验证已在各自的esp_tls_cfg_t结构中设置的所有 ESP-TLS 连接的服务器。
static void https_get_request_using_global_ca_store(void)
{
    esp_err_t esp_ret = ESP_FAIL;
    ESP_LOGI(TAG, "https_request using global ca_store");
    /*使用wolfssl设置全局CA存储数据TLS/SSL*/
    esp_ret = esp_tls_set_global_ca_store(server_root_cert_pem_start, server_root_cert_pem_end - server_root_cert_pem_start);
    if (esp_ret != ESP_OK) {
        ESP_LOGE(TAG, "Error in setting the global ca store: [%02X] (%s),could not complete the https_request using global_ca_store", esp_ret, esp_err_to_name(esp_ret));
        return;
    }
    /*所有连接使用全局ca_store*/
    esp_tls_cfg_t cfg = {
        .use_global_ca_store = true,
    };
    https_get_request(cfg);
    /*释放全局ca_store*/
    esp_tls_free_global_ca_store();
}

HTTP请求实现:

static void https_get_request(esp_tls_cfg_t cfg)
{
    char buf[512];
    int ret, len;

    /*1.建立https连接*/
    struct esp_tls *tls = esp_tls_conn_http_new(WEB_URL, &cfg);

    if (tls != NULL) {
        ESP_LOGI(TAG, "Connection established...");
    } else {
        ESP_LOGE(TAG, "Connection failed...");
        goto exit;
    }

    size_t written_bytes = 0;
    /*写入http请求*/
    do {
        ret = esp_tls_conn_write(tls,
                                 REQUEST + written_bytes,
                                 sizeof(REQUEST) - written_bytes);
        if (ret >= 0) {
            ESP_LOGI(TAG, "%d bytes written", ret);
            written_bytes += ret;
        } else if (ret != ESP_TLS_ERR_SSL_WANT_READ  && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
            ESP_LOGE(TAG, "esp_tls_conn_write  returned: [0x%02X](%s)", ret, esp_err_to_name(ret));
            goto exit;
        }
    } while (written_bytes < sizeof(REQUEST));

    ESP_LOGI(TAG, "Reading HTTP response...");
    
	/*3等待服务器回应*/
    do {
        len = sizeof(buf) - 1;
        bzero(buf, sizeof(buf));
        ret = esp_tls_conn_read(tls, (char *)buf, len);

        if (ret == ESP_TLS_ERR_SSL_WANT_WRITE  || ret == ESP_TLS_ERR_SSL_WANT_READ) {
            continue;
        }

        if (ret < 0) {
            ESP_LOGE(TAG, "esp_tls_conn_read  returned [-0x%02X](%s)", -ret, esp_err_to_name(ret));
            break;
        }

        if (ret == 0) {
            ESP_LOGI(TAG, "connection closed");
            break;
        }

        len = ret;
        ESP_LOGD(TAG, "%d bytes read", len);
        /* Print response directly to stdout as it is read */
        for (int i = 0; i < len; i++) {
            putchar(buf[i]);
        }
        putchar('\n'); // JSON output doesn't have a newline at end
    } while (1);

exit:
    esp_tls_conn_delete(tls);
    for (int countdown = 10; countdown >= 0; countdown--) {
        ESP_LOGI(TAG, "%d...", countdown);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

你可能感兴趣的:(ESP32系列教程,网络,服务器,http)