Enrolling your own firmware
Without Connhex Edge, enrollment is one HTTPS call.
Replace connhex.com with your Connhex domain throughout.
Try it from a laptop
Everything below runs with openssl, curl and jq, so you can prove the flow end to end before writing any firmware.
# 1. Private key, created locally and never sent anywhere
openssl ecparam -name prime256v1 -genkey -noout -out device.key
# 2. Signing request. The subject does not matter: Connhex names the certificate
openssl req -new -key device.key -subj "/CN=device" -out device.csr
# 3. Enroll
curl -X POST https://edge.connhex.com/things/enroll \
-H "Authorization: Claim <claim key>" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg csr "$(cat device.csr)" \
'{registration_id: "SN-000123", csr: $csr,
attributes: {hostname: "line-3-gateway"}}')" \
-o identity.json
# 4. Connect with what came back
jq -r .client_cert identity.json > client.crt
jq -r '.ca_chain // .ca_cert' identity.json > ca.pem
EVENTS=$(jq -r '.connhex_channels[] | select(.metadata.type == "event") | .id' identity.json)
mosquitto_pub --cafile ca.pem --cert client.crt --key device.key \
-h edge.connhex.com -p 8883 \
-u "$(jq -r .connhex_id identity.json)" \
-P "$(jq -r .connhex_key identity.json)" \
-t "channels/$EVENTS/messages/events/data" \
-m '[{"n":"hello","v":1}]'
Topics, subtopics and payload formats are covered in Manually send MQTT messages.
The enrollment request
POST https://edge.connhex.com/things/enroll
| Field | Meaning |
|---|---|
registration_id | What the device asks to be known by: its serial, MAC, or machine id. Up to 256 bytes |
csr | The PEM certificate signing request |
attributes | Optional. Anything you want the operator to see in the queue, such as hostname, board revision, current firmware version |
profile_id | Required only for profiles using per-device derived keys |
Authenticate with one of:
Authorization: Claim <claim key>for a device joining through a provisioning profileAuthorization: Thing <init_key>for a device you registered in advance, whereinit_keyis the key shown on that device's page
Under per-device derived keys, a device's key is HMAC-SHA256(group key, registration_id) and the request has to name the profile.
The answers
200 carries the device's identity:
{
"init_id": "SN-000123", // the id Connhex assigned
"init_key": "…", // claim enrollments only
"connhex_id": "…", // MQTT username
"connhex_key": "…", // MQTT password
"connhex_channels": [ … ], // event and control channels
"client_cert": "-----BEGIN CERTIFICATE-----…",
"ca_cert": "…", "ca_chain": "…",
"ota_token": "…", // optional managed update credential
"iot_host": "edge.connhex.com",
}
From here on the device authenticates with what it just received. The certificate and its key sign every HTTPS request it makes and the TLS handshake to the broker. The enrollment credential is not presented again unless the device has to enroll from scratch.
The provision or enrollment token used for this exchange and the returned ota_token do different jobs. The provision token authorizes enrollment. The OTA token belongs to the resulting device and is used by a system-image updater configured with TargetToken. One cannot be substituted for the other.
ota_token is optional. A native updater uses its client certificate and does not need this field. Connhex continues to issue, rotate and revoke OTA tokens for system-image updaters that cannot present a certificate. See System image updates.
202 means the profile wants the device approved first:
{ "status": "pending", "enrollment_id": "enr_…" }
Poll GET /things/enroll/<enrollment_id>/status with the same credential every 30 seconds, adding ?profile_id=… if you sent one. It answers 202 while waiting, 200 with the identity once approved, 403 {"status":"rejected"} if an operator turned the device down, and 410 if the identity has already been collected.
403 covers everything else, with no detail: a bad key, an unknown or disabled profile, a serial that is not on the allowlist, an exhausted quota. Devices are told the same thing in every case so that a stolen key learns nothing by probing. The real reason is on the profile's activity page.
Store the identity
Keep the response, the private key and the certificate on persistent storage. The full bundle is what makes the identity portable: a device that later runs Connhex Edge or a system image updater finds everything it needs there and carries on as the same device, with no second enrollment.
/data/connhex/identity.json the enrollment response, verbatim
/data/connhex/device.key the private key
/data/connhex/client.crt the certificate
/data/connhex/ca.pem the CA chain
Use 0600 and root ownership. On a device that flashes whole system images, this has to sit on a partition that survives the swap.
Renew before expiry
Certificates last 30 days. Once two thirds of that has passed, generate a fresh key pair and signing request, and send it with the certificate you still hold:
curl -X POST https://edge.connhex.com/things/renew \
--cert client.crt --key device.key \
-H "Content-Type: application/json" \
-d "$(jq -n --arg csr "$(cat renew.csr)" '{csr: $csr}')"
The response carries the new certificate and CA chain. Replace both files, keep the new key, reconnect.
A device that sleeps past expiry can no longer renew and must enroll again with the credential it still holds, arriving in the queue as a replacement. See Certificate lifecycle.
Re-read the configuration
GET https://edge.connhex.com/things/config, returns the device's configuration: broker credentials, channel ids, CA chain and the template from its model. Certificate material and secrets are never included in the response.
Call it at boot to pick up configuration changes.
On constrained hardware
- ECC P-256 is recommended. P-384 and RSA 2048 or larger also work.
- mbedTLS and wolfSSL cover key generation and signing requests on virtually any MCU, and every common secure element does both in hardware.
- The device needs roughly correct time, or no certificate will validate.
- Hardware that genuinely cannot generate a key can have Connhex generate one instead. See Device identity.