Securely exchange a bootstrap "claim" certificate (shared across a fleet) for a unique per-device certificate when the device first connects. The per-device certificate is then used for all subsequent AWS IoT operations.
This module implements provisioning by claim. Provisioning by trusted user is out of scope.
See the AWS IoT Fleet Provisioning documentation for service architecture, IAM/policy setup, and pre-provisioning hooks.
Done once, before devices are deployed:
CreateProvisioningTemplate— creates a fleet-provisioning template (also doable in the AWS IoT console under Onboard → Fleet provisioning templates).- Generate a claim certificate + private key.
- Register the claim certificate with AWS IoT and attach an IoT policy that restricts the certificate to provisioning operations only.
- Attach the
AWSIoTThingsRegistrationmanaged policy to the provisioning IAM role. - Manufacture devices with the claim certificate + private key embedded.
tests/provisioning_infrastructure/ contains the basic AWS infrastructure
files (template, policy, pre-provisioning Lambda) used by the integration
test. Note: the example pre-provisioning hook blindly accepts any
request — replace it with your own validation logic in production.
- Connect to AWS IoT MQTT using the claim certificate.
- Call
FleetProvisioner::provision(orprovision_csr/provision_cbor). The function:- Subscribes to the create-cert + register-thing reply topics.
- Publishes
CreateKeysAndCertificate(orCreateCertificateFromCsr). - Receives the new certificate; calls your
CredentialHandlerto persist it. - Publishes
RegisterThingwith the template name, ownership token, and any user-supplied parameters. - Returns the provisioned device configuration (deserialized into your
own
DeserializeOwnedtype) on success.
- Reconnect with the new per-device certificate.
| Direction | Purpose | Topic |
|---|---|---|
| Pub | Create keys & certificate | $aws/certificates/create/{format} |
| Sub | CreateKeys reply | $aws/certificates/create/{format}/accepted / .../rejected |
| Pub | Create certificate from CSR | $aws/certificates/create-from-csr/{format} |
| Sub | CreateFromCsr reply | $aws/certificates/create-from-csr/{format}/accepted / .../rejected |
| Pub | Register thing | $aws/provisioning-templates/{template}/provision/{format} |
| Sub | RegisterThing reply | $aws/provisioning-templates/{template}/provision/{format}/accepted / .../rejected |
{format} is cbor when the provision_cbor feature is enabled, json
otherwise. The CBOR variant uses smaller wire payloads at the cost of pulling
in minicbor.
use rustot::provisioning::{Credentials, CredentialHandler, Error, FleetProvisioner};
#[derive(Default)]
struct Storage { /* ... */ }
impl CredentialHandler for Storage {
async fn store_credentials(&mut self, c: Credentials<'_>) -> Result<(), Error> {
// Persist `c.certificate_pem`, `c.private_key`, `c.certificate_id`
// somewhere durable (flash, secure element, etc.).
Ok(())
}
}
#[derive(serde::Deserialize)]
struct DeviceConfig {
thing_name: heapless::String<128>,
// ... whatever your provisioning template emits ...
}
async fn provision<M: rustot::mqtt::MqttClient>(mqtt: &M) {
let mut storage = Storage::default();
let parameters = serde_json::json!({
"SerialNumber": "1234567890",
"DeviceLocation": "factory-7",
});
let cfg: Option<DeviceConfig> = FleetProvisioner::provision(
mqtt,
"MyProvisioningTemplate",
Some(¶meters),
&mut storage,
)
.await
.expect("provisioning");
if let Some(cfg) = cfg {
log::info!("Provisioned as {}", cfg.thing_name);
}
}For higher security, generate the private key on-device and submit a CSR via
provision_csr — the private key never leaves the device.
| Feature | Default | Effect |
|---|---|---|
provision_cbor |
yes | Encode payloads as CBOR via minicbor-serde; topic suffix becomes cbor. Disable for JSON. |
When enabled, FleetProvisioner::provision_cbor is also available
explicitly; otherwise only the JSON entry points compile.
- By-claim only. Provisioning by trusted user is not implemented.
- Single attempt. No automatic retry / backoff; wrap calls if you need it.
- No certificate rotation. Once provisioned, the device uses the issued certificate; rotation is a separate AWS feature.
Unit tests cover topic format/parse and request/response serialization:
cargo test --lib provisioning::The integration test (tests/provisioning.rs) runs against a real AWS IoT
endpoint:
RUST_LOG=trace \
TEMPLATE_NAME=provision_template_name \
THING_NAME=MyTestThing \
AWS_HOSTNAME=xxxxxxxx-ats.iot.eu-west-1.amazonaws.com \
cargo test --test provisioning --features "provision_cbor,log"tests/secrets/claim_identity.pfx must hold the claim certificate. If
password-protected, supply IDENTITY_PASSWORD.
pfx files can be built from a certificate + private key with:
openssl pkcs12 -export \
-out claim_identity.pfx \
-inkey private.pem.key -in certificate.pem.crt -certfile root-ca.pem