feat(esp_http_client_mutual_auth): add mutual TLS example

Closes https://github.com/espressif/esp-idf/issues/18393
This commit is contained in:
Ashish Sharma
2026-05-29 11:58:25 +08:00
parent fb2e11f070
commit c6e86872d6
24 changed files with 811 additions and 0 deletions
@@ -0,0 +1,14 @@
set(requires esp-tls nvs_flash esp_event esp_netif esp_http_client)
set(embed_txt certs/ca_cert.pem)
if(CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS)
list(APPEND requires esp_secure_cert_mgr)
else()
list(APPEND embed_txt certs/client_cert.pem certs/client_key.pem)
endif()
idf_component_register(SRCS "mutual_auth_example.c"
INCLUDE_DIRS "."
PRIV_REQUIRES ${requires}
EMBED_TXTFILES ${embed_txt})
@@ -0,0 +1,12 @@
menu "Example Configuration"
config EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
bool "Use DS peripheral for client authentication" if SOC_DIG_SIGN_SUPPORTED
default n
help
When enabled, the client private key is held by the DS (Digital Signature)
peripheral. The client certificate and DS context are loaded from the
esp_secure_cert partition at runtime. Requires provisioning; see the README.
When disabled (default), the client certificate and private key are embedded
in the firmware as PEM files from main/certs/.
endmenu
@@ -0,0 +1,41 @@
# Certificate Generation for Mutual TLS Example
All certificates are pre-generated with a 10-year lifetime. To regenerate, run
the following OpenSSL commands from this directory (`main/certs/`).
## Generate CA (signs both client and server certs)
openssl req -newkey rsa:2048 -nodes -keyout ca_key.pem -x509 -days 3650 \
-out ca_cert.pem -subj "/CN=Mutual Auth Test CA"
## Generate client certificate (embedded in firmware)
openssl genrsa -out client_key.pem 2048
openssl req -out client.csr -key client_key.pem -new -subj "/CN=esp_mutual_auth_client"
openssl x509 -req -days 3650 -in client.csr -CA ca_cert.pem -CAkey ca_key.pem \
-sha256 -CAcreateserial -out client_cert.pem
rm client.csr ca_cert.srl
## Generate server certificate (used by pytest mTLS server)
openssl genrsa -out server_key.pem 2048
openssl req -out server.csr -key server_key.pem -new -subj "/CN=esp_mutual_auth_server"
openssl x509 -req -days 3650 -in server.csr -CA ca_cert.pem -CAkey ca_key.pem \
-sha256 -CAcreateserial -out server_cert.pem
rm server.csr ca_cert.srl
## Files
| File | Purpose | Embedded in firmware? |
|------|---------|----------------------|
| `ca_cert.pem` | CA certificate (verifies server identity) | Yes |
| `ca_key.pem` | CA private key (signs certs; keep private) | No |
| `client_cert.pem` | Client certificate (sent to server) | Yes (software mode) |
| `client_key.pem` | Client private key | Yes (software mode) |
| `server_cert.pem` | Server certificate (pytest mTLS server) | No |
| `server_key.pem` | Server private key (pytest mTLS server) | No |
## DS peripheral mode
For DS peripheral mode, see `esp_secure_cert_data/` and the DS provisioning
section in the top-level README.
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIUHrbEhg+kRb2wd66oi57asPHvrBYwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjAyMDQwNzMwMDRaFw0zNjAyMDIw
NzMwMDRaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCpug883pCJKGj0cZ/zdJLnjBa9M6lPWscgDFh1wPO1wyOzu25Q
kUykgUE8GE3W7RC4zw4LCUkU06EeVnp6UkXsKktC94OvAeYczsq6Z95WwyF8ktXP
AzW/Lpz202MFpA4edcoczLJEf1GPmoI1RM//V19Op+6nLBNLLTj35yO3CUMjxeh6
ptlxi3PbiFliOVff6DfPcNzp7rh1k+SWoNP5adTN8aHjpoO53wFHhBfiP4zqnLJG
uHFHApGcwyf1SAbERJ4F2tV+xMRVnp2i/Bx9pIY7qVRKdqOswQK6YGSlznfY8RCM
he9B0mZpSa9NFsPdm+Lpt2wxDvuSC/WtMLHBAgMBAAGjUzBRMB0GA1UdDgQWBBRG
ECXpPzDsNPv55ZHXAsKgCIYF6zAfBgNVHSMEGDAWgBRGECXpPzDsNPv55ZHXAsKg
CIYF6zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAFiLooiqC/
LQBoN7J6FyQhsKvly+yX4Vh8ituhQQnt4kRD28ci+GmceqgNlX86e/MMTzTXOCMJ
EIhR4xHd1xg5lnPLPS1o+LXsbyOlU8QTCoYgVkjDVHi4fGnbMIoWaZBt8o3MYgAy
Fc+7A6ZoK9PIbbcRUYPSPGe1loop490tgyNrvyc29lLdg4ljO+Xg3Wpu95bTxe/F
bjWFWZgF6aqAfrT7QZXk9OIfOkDpcb5EsxBySQIZNC06FEtNkwanVWFSMdDm3Axh
Xjuv1HdGyHnS/jcFTZ4EfB76oU0+NuoYtDYF7Fns3Upvg8QZhhs+6Ob5mRKJefRD
HlIA+c6CBmsY
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpug883pCJKGj0
cZ/zdJLnjBa9M6lPWscgDFh1wPO1wyOzu25QkUykgUE8GE3W7RC4zw4LCUkU06Ee
Vnp6UkXsKktC94OvAeYczsq6Z95WwyF8ktXPAzW/Lpz202MFpA4edcoczLJEf1GP
moI1RM//V19Op+6nLBNLLTj35yO3CUMjxeh6ptlxi3PbiFliOVff6DfPcNzp7rh1
k+SWoNP5adTN8aHjpoO53wFHhBfiP4zqnLJGuHFHApGcwyf1SAbERJ4F2tV+xMRV
np2i/Bx9pIY7qVRKdqOswQK6YGSlznfY8RCMhe9B0mZpSa9NFsPdm+Lpt2wxDvuS
C/WtMLHBAgMBAAECggEAAt0DUwvvrQqwG5ibodIwhMS/oPVBSNgPli4zI1hsHB/E
x0xVD/mljPxrvvFrhcHV14JRurSvRZFM7Wu40P24lYOApcpyb6ZE7S09bQ/hx72u
v9Dj18RWeKlXB1B5Yg/al5+1107KPp7Vv8oT4oVsy2JcVqG9ZFdZY3optP/yoay4
oRJprOV4jc8aaLB0+zze/7RJZ4DtpeOWhyNws/cGwwFvaDlncnylAoaDs20MBfsg
VV+glo69rqvn3SCfSXIG5/vyfHS52xPN04kPrpXpu7kvTeJHqCfq4w5LSS0GgBdM
5CbBSpW25AnX9Jqczxhu7Y2rt8kIx+4gnW05z0YB2QKBgQDn9yeQzsXK98R7R6hz
ogKyOYQuNVNjjR7guSD0285QTU+XB8jC2nqiTVPy8Xy1w6udh9SjvqS47yQyySzC
ZCFvT3rwwdWP5pUj89FG7XoX33hQrV4+jbBCcjajeyMxAmMM83ISL9NahsVLWx7K
gdPdxhBiCB8dueF+kVcBf1Bb2QKBgQC7UAjwl45oOIDoXBqN3TDGVodwQM6gn1rY
heYjEUeQmjL6j4e8PiUFl5rHCNavIUr0gkBhnN5hcq2mvfECBEVVm8dlrvHHa4Ti
ujhC6DGgrOUIPok+rXJoELvmrYa72H04uHwkOkESkuHO+gHuRJ7nIxH/BktGKsol
Unp+CNBcKQKBgQCtn+NvkjWeTII2vFYr5xIZkM+NPsDh/Nkl36v5WyU8GgH+zAbL
QnkUTskNSQ/NhV5JFUhmH+Zvvh/cG5RzFDuqc1VUK+HMSg1L0c3NRydiAxStXnby
X1+U/KRFDYAzyNOW+Alj74RFeCbo1pVfgnmwv/W3StjviRhtgiAbsM3XUQKBgQCg
E94P/vWtC9zetxfadVXhqsFEpZ3wlz9EG+p5vaKzaZR3nYIa1eE9zjpwLpWKRaGR
JF9xDGbgUOkmvtzhJFU8vEzEEaZ/DtwaB5tdUqJW9msliIwyDHjhhquOkG28y174
wnEVZNOH1A82m2JbBjnmvon6sJ5T8O2gx8P3QgEPQQKBgQCcpSf/rJxMfEeZf4Kl
EeqSjPwFRKsC/8gBs63VJrmYh2XigL5tI2MnTdgts+4W1xC8avPiRoeaZ8YN6258
n2tVyhJrm3F1cw/tFmLE0kuNkS0jOA/hzFeOkLJTZXR9npBzwNduQQ5i0HqQM9Qi
aIIVlhLZ0Jlqu6SLnJ74n34VnA==
-----END PRIVATE KEY-----
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBDCCAeygAwIBAgIUa91CxumwjyUKK9AHnJ9upekrqlgwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjA1MjkwMzI5MTlaFw0zNjA1MjYw
MzI5MTlaMCIxIDAeBgNVBAMMF2VzcF9odHRwX2NsaWVudF9kc190ZXN0MIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsztCh5DaIh+KDzk6L5CEps8AJ6cw
xJffL/wjZc3yTOCjwDOX7UdDilizLobkzCBMm8lccafts2+jEAVaMrR1pQVO0c8J
5sDk1IHlNJVlq1UxnIl7JvbDjtAWp+cHYMRQSBXmHrkqJl45LPTdjC0FvdsB2oyj
HFmDHp3A3B/BwzdwyBzucG+Xa3OILZI0wsri0oDMyTw6Xg6SoXmLTQhbqCD38Ijt
d9AalLMElM+2j+IH5zVlnG+JpCrvzYy7pY7gIUmjLFBewSpEiPjxWjrFJ0teXyTj
smhd/IK5rVQw2v1wjKT1Wc5lgRDifZTVNRxDIepr+m4Ck3clRfyesSslhQIDAQAB
o0IwQDAdBgNVHQ4EFgQU7jBC4N9rFhRFkzBq0KcXGRsNatcwHwYDVR0jBBgwFoAU
RhAl6T8w7DT7+eWR1wLCoAiGBeswDQYJKoZIhvcNAQELBQADggEBAHWHKGJ0Z4+b
rw25oZ9ZK2pZkQcvAG1HBQxHoNBRmYruitvALnQhq7UuHlAoMFxBmkJ61FRjgA1x
zGAZsfTCy/Agv2lfVf6LiRd+Rl134rzHLlCoTg0tZC5uFcS/6W5mbfXIviYaPRaf
RMgpM1ZPYMF5VsX0B6CJH6I+UHoZVLTb1nz5gu01WiKKULTygW70xyk5B0osiK9C
UDCOQ7jcYrjYNZulwY2qv+Kg3PH6aFR9xT+RUp1fDb0HJk0/M0YDOdh4QUzm29UL
KCCM+E25tan4Nvgg1jQIw3dppWc6dCSE0IgCCVY+O3aZqnfRugHl7NCxJ5pHCyPr
zV9BSaFx8Uk=
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzO0KHkNoiH4oP
OTovkISmzwAnpzDEl98v/CNlzfJM4KPAM5ftR0OKWLMuhuTMIEybyVxxp+2zb6MQ
BVoytHWlBU7RzwnmwOTUgeU0lWWrVTGciXsm9sOO0Ban5wdgxFBIFeYeuSomXjks
9N2MLQW92wHajKMcWYMencDcH8HDN3DIHO5wb5drc4gtkjTCyuLSgMzJPDpeDpKh
eYtNCFuoIPfwiO130BqUswSUz7aP4gfnNWWcb4mkKu/NjLuljuAhSaMsUF7BKkSI
+PFaOsUnS15fJOOyaF38grmtVDDa/XCMpPVZzmWBEOJ9lNU1HEMh6mv6bgKTdyVF
/J6xKyWFAgMBAAECggEABhTu3dUbTx4dMVq37+Y/rL4IcNAFC8llgDuA41L43lqf
DoYRqTJfsdbvBEH9Nd9WCiaWivVvpO+XAROqwF626+nBXMWMPfbHV7NYA4/wOmny
fC/UFySMypCivCwkPXMxOt5Mso0w8iAEtlL8Hjtm3vmKrLpUoevNqGEiVWCEfYBv
h7Yc7uKcB90WyZ3LtgmghV46kDFvaSMMdPTqVU6jgCTpq6yBCntpeYz1VqcnDhZt
nU6i/vinJz8BMDXIDbVonie0lZ9tCWnYwHaWCkIPy9Cb5eqlldJfiN1CYz9MFGGs
oymeTF+lrtPphRTakpziDkxBM/AwnIkx5HQTsxF4jwKBgQD882SmrGvbshJLYMVl
4UznqDoP59M7G5kwQ/j579VKkhbrKrV0JvOEYQuJx18d7drDyY7eLRF6KYRLYOhG
FWznMpNhRYQMcWcAqNLHb5W4FSsGGRt8Q1qmmKQmO192FZVveG0czz2FAPY4uSBX
4va+mJszMrnfEApqW8TdOrEwYwKBgQC1ZF5qTQR9wZPj+65mGwfYei1EP1OEE5xc
DWo9fsdDVxv4ykHTpURF+EftMkOF+b/GTgJHyEceZfgm/9dx15EeQuHqpNDjffUD
y/b8GVT7NB2nR2m3ztXYBHsRK7uYGbgVtB+7w1gFpcZRx92SvITy+EjQRTs9z4n/
Ru2y8P+S9wKBgF0bTfIXxz+/xQIf5akBjCg9ANo378Vy/CkK7As7n1vqeCsptk7B
w6L3gaK+UyGWGo80krTvC97Wh06jpfueCU70i9EjIF7gIxTYD3W/efGfQQ3mkfpk
ZGqsBsfX1OSHP1Efl7IiCjf5yafJZMFU1pQDYiUvR8F2iw7pJoZ0AyKXAoGBAKKv
RCaesLqBFTzSC5Y0BBNZcKPXD/ZTCFdfCLviqqBwzfuSmvtRLCx9AzVvcTQFzMP0
TwNGUtKmrat8piPKLLMxVSF3dImz/D3NftSXe6pZEYdn+x8JeK1nR2EdEgDWgE2m
4RcrmhRmm7nZQZZLUgoAOH3iucE0FBZJ7QIiN3X9AoGBAJmGvBoa6ZQpbRXZbVfv
qvz+gzwN2fO3t7iWGaHVCGygnSaplCGl/MfVtb/ak3AG5NcjwDqjld8X7RE9dSk2
l12Y3AtsyaDozLg5KXdwt+F1Pdhju9WB3pd5O25Ocn5JU1jUqL+huW+d7WhUDPO7
PHTgtfI15eDu5LinkNPD73hE
-----END PRIVATE KEY-----
@@ -0,0 +1 @@
�¾Bÿ›Ä”<|S¾ßpt3®Nk”sc† dø�à
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIUcDdlf0FDC5KPEcTnJG58JcXKGtAwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjA0MDgwMzAzMzdaFw0zNjA0MDUw
MzAzMzdaMCExHzAdBgNVBAMMFmVzcF9tdXR1YWxfYXV0aF9zZXJ2ZXIwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4AfXPbz06C1kwJHSkMWgzVHgz85Pb
bF7aRzImfHR9nfz2hNJ2Yg8Apdxt/xWM1n2uDDlVWjg0vK50X3DK4oz1Lk32//IE
iYajZuiFsO6ZBNHgy1rehJUlk92Z4fklhpEgpVT67Ph6o8yhPDjNgPM03eJsl6yC
lawxETSXU+dOhjzfuiT0IA74UOhTVIfVe8E0AlXGqjrN5QkKP5Xb74w1ovIZPOzy
vWNjnzuwg2MKWHTFOolPpKft4GUqPvMD41qNfymUL46JxtONcKwx//Q5hO0bSAnB
GJ1Brqca366ObF6Xh+dAXALnxVkbpauoJ7ZqdyiJg6YANovawe/oZZXpAgMBAAGj
QjBAMB0GA1UdDgQWBBTOXVwKpLmhvv/2Nqe9E4Hx8VYsrzAfBgNVHSMEGDAWgBRG
ECXpPzDsNPv55ZHXAsKgCIYF6zANBgkqhkiG9w0BAQsFAAOCAQEAdgeCBgBgu8N7
r9ycEytvs1AKxf2P3c6VFOlXF8rRNq/+8a8CoyGm4cF8u7AyvQQJZya6Luv+xg+/
tguXIbw5goNenNkEuUOHRlo39vZ/pHe/HGCAxHatWG8qgoahXCZh3OSEmHfDwEDc
RbYTJXGb98VynFzuxQ4RSDXb+WxhslwGCV2NXrF6gLS1FgZDKpJChmhkvw/ubdxY
QS3fSToWlvs7PSyZi2Ei5XuGfthQPT+PZpyHZufXX9evLDdGOB/5SOz+p0SSBXcZ
he21O0jovx0RDSnnq6kdG+qHfUrwOt5TGJ8tIJQZuKi9VUIMEONC7RMV4u1T00j9
8VtZEG2gfg==
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4AfXPbz06C1kw
JHSkMWgzVHgz85PbbF7aRzImfHR9nfz2hNJ2Yg8Apdxt/xWM1n2uDDlVWjg0vK50
X3DK4oz1Lk32//IEiYajZuiFsO6ZBNHgy1rehJUlk92Z4fklhpEgpVT67Ph6o8yh
PDjNgPM03eJsl6yClawxETSXU+dOhjzfuiT0IA74UOhTVIfVe8E0AlXGqjrN5QkK
P5Xb74w1ovIZPOzyvWNjnzuwg2MKWHTFOolPpKft4GUqPvMD41qNfymUL46JxtON
cKwx//Q5hO0bSAnBGJ1Brqca366ObF6Xh+dAXALnxVkbpauoJ7ZqdyiJg6YANova
we/oZZXpAgMBAAECggEAHNtEgaUP9K6w7EmF9/Xo7HEpGfnvTHBAqSSUgHqbXiki
RvAU0rdADmRJyapA5pcAm1npZ9w/3O1gwnlGhrAOq6ijq2z/Yadw85ErkpkTt0WN
Ox6eVS/KSrFXWvmYM+1YNxwWfp9zvEU8eGX/ASNhgKnyB1ZQmc3/ygDJFEOhOQFe
rAMntZvoEiQkkQIL45iKLceFfnTQ4iJNLRDvkOxt98OYgjOkVfLZvYXgTTH0r1mn
x2M3ITVVcU4OGs5AFQgbV/9fB5raPCAnroQkRIG69XD+uOJmRX04WvW8Hk9BQXk0
aAkPSa5yPdZbpjsV5aYaQYoQodblN2XKuVxoqmj6bQKBgQD+Bo5lDmhxNGC7x64B
6pTLAmSM2oT/mHRGRUyR+YB/Mf3mw5MhoBwZS5CuB1UaOaFBUx0b8SpfXfXMAhcF
483FcyYKK3B/ZV9u3EfvxCBiiLzwYqLQYaBp7efeQkAJzb+sIj0+ZE70Lh2IjspL
zhFh7WaMaxeU1IAfqvfRaATjewKBgQC5cBY19erRuztFkP2SeNYHlIj0Y1uF6QRX
8ds2QLQsnj7J4WU/5VgTKWTywx7gn82/hWcJAs2k1PEUspRfMLwpChtOxvYHEosM
4a9fCBKlA+KPc4NpSGaC2M9VoJIJrgo7JjzJMFS6qSJ3zbmWUQh1XCLliNWNey2s
LkiNrA0M6wKBgQCG5wxv9nrYw6wrjRuHwQBL33VuqA3Bf0EgoGTNkOcApZflGS/l
x5WkiVDIWvSC/N/6RR1MXYLXKpsCQInhgt0gYsps1CzmOvu3cBxz5IAeU+ei8X7t
kysRllpw2lYP3shPrc9AdxzG6Eae4tXj9AefLegr4iOf0kpIhw8cklUmSQKBgQC1
5qRy9DMO3unqeKq0poHU17hsepZJymSvXBjbpCbZabVP1SC7x95YlY9nr003rKpo
B5UlurE80oFV+0MeCTFZ1IcrBHJMR71MuomL3+BiLGhurTIn8ZRVIBZp+WOnyShS
E1UnSZijrcuY154IPJ7eeK3mmQ5ahY0szA3xoub+VwKBgQDUqNuMGgDmZo5O2Jsz
0J43BbRfqWXyQ9Q/nQQftHoxPEWkEQSoa3y58zWlYJKN4JoUU/UNR7dqdP5rnbhZ
f/pPXWdmhWQQlImgAfqJNEAZscA2NgsW/BOcot7zl/ZMgMOyDNXHoDSHenKPksKb
9POv9y/PnUWSUfG/WFwMEH65iQ==
-----END PRIVATE KEY-----
@@ -0,0 +1,7 @@
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
esp_secure_cert_mgr:
version: ">=2.0.0"
rules:
- if: "idf_version >=5.0"
@@ -0,0 +1,212 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ESP HTTP Client Mutual Authentication Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_http_client.h"
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
#include "esp_secure_cert_read.h"
#if CONFIG_MBEDTLS_VER_4_X_SUPPORT
#include "psa_crypto_driver_esp_rsa_ds_contexts.h"
#else
#include "esp_rsa_sign_alt.h"
#endif
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "MUTUAL_AUTH";
#define URL_BUF_SIZE 256
/* CA cert for server verification (embedded in both modes) */
extern const char ca_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const char ca_cert_pem_end[] asm("_binary_ca_cert_pem_end");
#if !CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
/* Software mode: client cert and key embedded as PEM */
extern const char client_cert_pem_start[] asm("_binary_client_cert_pem_start");
extern const char client_cert_pem_end[] asm("_binary_client_cert_pem_end");
extern const char client_key_pem_start[] asm("_binary_client_key_pem_start");
extern const char client_key_pem_end[] asm("_binary_client_key_pem_end");
#endif
/**
* Read the server URL from stdin. Used by pytest to inject the mTLS server address.
* Returns ESP_OK on success, ESP_FAIL on error or empty input.
*/
static esp_err_t read_url_from_stdin(char *url_buf, size_t buf_size)
{
example_configure_stdin_stdout();
ESP_LOGI(TAG, "Enter mutual auth server URL:");
if (fgets(url_buf, (int)buf_size, stdin) == NULL) {
ESP_LOGE(TAG, "Failed to read URL from stdin");
return ESP_FAIL;
}
/* Strip trailing newline */
size_t len = strlen(url_buf);
if (len > 0 && url_buf[len - 1] == '\n') {
url_buf[--len] = '\0';
}
if (len == 0) {
ESP_LOGE(TAG, "Empty URL");
return ESP_FAIL;
}
return ESP_OK;
}
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
/**
* DS peripheral mode: load client cert and DS context from esp_secure_cert partition.
*/
static void mutual_auth_with_ds(const char *url)
{
char *dev_cert_buf = NULL;
uint32_t dev_cert_len = 0;
esp_ds_data_ctx_t *ds_ctx = NULL;
esp_err_t ret = esp_secure_cert_get_device_cert(&dev_cert_buf, &dev_cert_len);
if (ret != ESP_OK || dev_cert_buf == NULL || dev_cert_len == 0) {
ESP_LOGE(TAG, "Failed to get device cert: %s", esp_err_to_name(ret));
return;
}
ds_ctx = esp_secure_cert_get_ds_ctx();
if (ds_ctx == NULL) {
ESP_LOGE(TAG, "Failed to get DS context");
esp_secure_cert_free_device_cert(dev_cert_buf);
return;
}
size_t ca_cert_len = ca_cert_pem_end - ca_cert_pem_start;
esp_http_client_config_t config = {
.url = url,
.cert_pem = ca_cert_pem_start,
.cert_len = ca_cert_len,
.client_cert_pem = dev_cert_buf,
.client_cert_len = dev_cert_len,
.ds_data = ds_ctx,
.skip_cert_common_name_check = true,
.timeout_ms = 10000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to init HTTP client");
goto cleanup;
}
esp_err_t err = esp_http_client_perform(client);
int status = esp_http_client_get_status_code(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Mutual Auth Status = %d, content_length = %" PRId64,
status, esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
cleanup:
esp_secure_cert_free_ds_ctx(ds_ctx);
esp_secure_cert_free_device_cert(dev_cert_buf);
}
#else /* Software key mode */
/**
* Software key mode: use embedded PEM client cert and key.
*/
static void mutual_auth_with_software_keys(const char *url)
{
size_t ca_cert_len = ca_cert_pem_end - ca_cert_pem_start;
size_t client_cert_len = client_cert_pem_end - client_cert_pem_start;
size_t client_key_len = client_key_pem_end - client_key_pem_start;
esp_http_client_config_t config = {
.url = url,
.cert_pem = ca_cert_pem_start,
.cert_len = ca_cert_len,
.client_cert_pem = client_cert_pem_start,
.client_cert_len = client_cert_len,
.client_key_pem = client_key_pem_start,
.client_key_len = client_key_len,
.skip_cert_common_name_check = true,
.timeout_ms = 10000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to init HTTP client");
return;
}
esp_err_t err = esp_http_client_perform(client);
int status = esp_http_client_get_status_code(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Mutual Auth Status = %d, content_length = %" PRId64,
status, esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
#endif /* CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS */
static void mutual_auth_task(void *pvParameters)
{
char url_buf[URL_BUF_SIZE];
if (read_url_from_stdin(url_buf, sizeof(url_buf)) != ESP_OK) {
goto done;
}
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
mutual_auth_with_ds(url_buf);
#else
mutual_auth_with_software_keys(url_buf);
#endif
done:
ESP_LOGI(TAG, "Finish mutual auth example");
vTaskDelete(NULL);
}
void app_main(void)
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
ESP_LOGI(TAG, "Connected, starting mutual auth example");
xTaskCreate(mutual_auth_task, "mutual_auth", 8192, NULL, 5, NULL);
}