Build1 publisher2 min readPublished
Fleet provisioning with CSR swaps a per-unit private key for one shared claim key in the image
The ESP32-S3 example generates the device key on the chip and sends only a CSR to AWS IoT Core. The credential that gets it there is a claim certificate and private key copied into the SPIFFS image every unit shares.
The Engineer · Build desk

What happened
- In AWS IoT fleet provisioning the device generates its private key on the chip and publishes a CSR to the CreateCertificateFromCsr API over MQTT to get a signed X.509 client certificate on first connection.
- The dev.to walkthrough downloads the claim pair from the AWS IoT console and copies it into claim_private.key and claim_cert.crt inside the example's spiffs_image/certs directory.
- The example provisioning policy scopes publish, receive and subscribe to the create-from-csr and template provision topics, and allows iot:Connect with Resource set to a wildcard.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- capability One firmware image can be flashed across a whole production run, because the station never has to inject anything unique to the unit in front of it.
- exposure A single extracted claim key is a fleet-level credential, so whatever protects one plaintext file in the build tree and in flash bounds provisioning security for every unit shipped.
- constraint Parts with a secure element that refuses imported keys cannot be provisioned by injection at all, so the CSR round trip is the only route to a unique identity on that hardware.
- decision Teams that need their own validity periods or signing algorithms have to take on self-managed signing and an external PKI or AWS Private CA, with the operational load that carries.
What crosses the network in this flow is a CSR, published over MQTT to the CreateCertificateFromCsr API [3]. AWS IoT Core signs it with an AWS-managed CA, or with a self-managed certificate provider tied to an external PKI or AWS Private CA, and returns the certificate [4]. A provisioning template then registers the Thing, attaches security policies and activates the certificate [5].
To make that first call the device needs a credential before it has one of its own. The guide uses a claim certificate, which it describes as a shared bootstrap X.509 certificate and private key pre-installed on devices during manufacturing [6]. So a secret still crosses the factory floor, one secret per image instead of one per unit [21].
In the walkthrough that secret is a file. You download the pair from the AWS IoT console and copy it into claim_private.key and claim_cert.crt under fleet_provisioning_with_csr/spiffs_image/certs [11], and the directory name says where it ends up. It will also sit in whatever your CI treats as a build input.
iot:Publish and iot:Receive are scoped to two topic ARNs, the create-from-csr topics and the named template's provision topics [13]. iot:Subscribe is scoped to the matching topicfilters [14]. iot:Connect has Resource set to "*" [12]. Three of the four actions are pinned to provisioning topics and the connect permission is not [20]. Read literally, a holder of that claim key can connect to the account's endpoint under any client ID and drive the create-from-csr exchange for that template [22].
The guide notes that fleet provisioning suits hardware using a secure element where private keys cannot be imported [8]. If the part will not accept an imported key, generating the key on the device is the only way to get a unique identity [2], and a CSR is the only way to get that identity signed. Signing policy is the other decision: self-managed signing lets you use your own PKI, validity periods and algorithms instead of relying solely on AWS-managed certificates [9].
The sequencing in the guide is sensible. It pins esp-idf v5.5.5 and the ESP32-S3-WROOM-1-N16R8 module, and clones esp-aws-iot at branch 202406.05-LTS-release with --recursive [15] [16]. It sets the target with idf.py set-target esp32s3 and builds [17] at step 2.2, before the provisioning policy is created at 2.3, so toolchain and submodule failures surface before any credential exists.
One more account-side dependency: the flow needs an IAM role, because AWS IoT itself needs permission to create, configure and register Things, certificates and policies on your behalf [19].
What to watch
- Whether AWS's own fleet provisioning policy examples keep iot:Connect at Resource "*", since that is the line people copy.
- Whether teams rotate or revoke claim certificates per production batch, rather than shipping one pair for the life of a product.
- Whether the esp-aws-iot example gains a path that generates the device key inside a secure element instead of in software.