Files
esp-idf/examples/protocols/esp_http_client_mutual_auth

Supported Targets ESP32 ESP32-C2 ESP32-C3 ESP32-C5 ESP32-C6 ESP32-C61 ESP32-H2 ESP32-P4 ESP32-S2 ESP32-S3

ESP HTTP Client Mutual TLS Authentication Example

This example demonstrates mutual TLS (mTLS) authentication using esp_http_client. The server verifies the client's identity via a client certificate, and the client verifies the server using a CA certificate.

Two client key modes are supported, selectable via Kconfig:

Mode Kconfig Client key source
Software keys (default) EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS=n PEM files embedded in firmware
DS peripheral EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS=y Hardware Digital Signature peripheral via esp_secure_cert partition

How it works

  1. Device connects to Wi-Fi or Ethernet (via protocol_examples_common)
  2. Prompts for HTTPS server URL on serial console
  3. Performs HTTPS GET with mutual TLS — sends client certificate to server
  4. Logs the HTTP status code

Quick start (software key mode)

idf.py set-target esp32c3
idf.py build flash monitor

When prompted, enter the URL of an HTTPS server that requires client certificates. The pre-generated certificates in main/certs/ are used automatically.

Certificates

Pre-generated certificates with 10-year lifetime are in main/certs/. A single CA signs both client and server certificates. See main/certs/README.md for OpenSSL commands to regenerate.

File Purpose Embedded in firmware?
ca_cert.pem CA certificate (verifies server) Yes (both modes)
client_cert.pem Client certificate Yes (software mode only)
client_key.pem Client private key Yes (software mode only)
server_cert.pem Server certificate (pytest) No
server_key.pem Server private key (pytest) No

DS peripheral mode

The DS (Digital Signature) peripheral holds the client private key in hardware. The client certificate and DS context are stored in the esp_secure_cert partition.

Prerequisites

  • ESP32-C3 or other chip with SOC_DIG_SIGN_SUPPORTED
  • esp-secure-cert-tool (pip install esp-secure-cert-tool)

Provisioning

  1. Generate DS partition and HMAC key (from the example directory):
configure_esp_secure_cert.py \
  --device-cert main/certs/client_cert.pem \
  --target_chip esp32c3 \
  --configure_ds --priv_key_algo RSA 2048 \
  --skip_flash --keep_ds_data_on_host \
  --private-key main/certs/client_key.pem \
  --efuse_key_id 1

Copy the output files to main/certs/esp_secure_cert_data/:

  • esp_secure_cert.bin
  • hmac_key.bin
  1. Burn HMAC key to eFuse:
# QEMU
idf.py qemu efuse-burn-key --do-not-confirm BLOCK_KEY1 \
  main/certs/esp_secure_cert_data/hmac_key.bin HMAC_DOWN_DIGITAL_SIGNATURE

# Hardware (irreversible!)
idf.py efuse-burn-key --do-not-confirm BLOCK_KEY1 \
  main/certs/esp_secure_cert_data/hmac_key.bin HMAC_DOWN_DIGITAL_SIGNATURE
  1. Build and flash:
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.qemu_ds" build flash monitor

QEMU testing

Three QEMU tests are included:

  • test_mutual_auth_software_keys — software key mTLS, expects HTTP 200
  • test_mutual_auth_ds — DS peripheral mTLS, expects HTTP 200
  • test_mutual_auth_ds_fails_wrong_credentials — DS with wrong server CA, expects failure

The tests start a local HTTPS server with mutual TLS, inject the URL via serial, and verify device output.

Troubleshooting

TLS handshake failed: BAD_SIGNATURE The client certificate is not signed by the CA the server expects. Regenerate certs using the commands in main/certs/README.md.

ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL The HMAC key in eFuse does not match the key used to create the DS partition. For QEMU, delete the eFuse image and regenerate it from the current hmac_key.bin.