# MQTT IoT device certificates

## Trust and authentication

MQTT requires mutual TLS. The existing HTTPS/MQTT server certificate still authenticates the server and its hostname. A separate self-signed **UIC Connect TMS IoT CA** signs individual device client certificates (RSA, SHA-256, client-authentication EKU, one-year lifetime bounded by the CA expiry). This CA is not the APK signing certificate or the portal HTTPS certificate.

The Android agent generates its private RSA key inside AndroidKeyStore. It submits only the public key, and downloads only the issued public certificate. The private key never traverses the network. TLS proves possession of that key. The broker additionally binds the exact SHA-256 certificate fingerprint to the enrolled device, assignment epoch and existing unique device credential. Anonymous and password-only MQTT connections are rejected.

## Bootstrap proof and provisioning

Android enrollment must securely supply the known device ID, serial, assignment epoch, unique device password, server trust certificate, HTTPS URL and `iotBootstrapKey` (32 random bytes encoded as 64 hexadecimal characters). The common bootstrap key is never embedded in source/APKs or shown by the installer. The current Android development transfer writes private app enrollment storage through USB debugging; production Android enrollment distribution is a separate administrative process.

The A10 Linux xTMSAgent performs first-run credential enrollment itself after a SuperAdmin onboards the hardware serial under the customer and UIC `A10-T` model. Its customer-specific installation package contains a protected bootstrap file with customer identity, server endpoints and the common bootstrap key, but no terminal Device Secret. The agent reads the hardware serial and full reported model, signs a short-lived claim for that existing record, receives its unique Device Secret, and atomically saves it with mode `0600`. It deletes the bootstrap file, provisions its client certificate and then connects to MQTT. A serial that is not onboarded is rejected; the terminal displays Enrollment pending and retries every 60 seconds or when Enter is pressed. The development terminal's `updateTMS.sh` only installs the package. Protect the customer package because extraction of the common bootstrap key remains a risk; MQTT and subsequent HTTPS access still require the independently issued device identity, secret and certificate.

`POST /device-iot/issue` is HTTPS-only and rate limited. The proof is AES-256-GCM with a random 12-byte IV, 16-byte tag, and AAD `UIC-TMS-IOT-v1`. The wire proof is IV + ciphertext + tag. Authenticated JSON includes deviceId, SHA-256 of trimmed uppercase serial, assignmentEpoch, SHA-256 of SubjectPublicKeyInfo, UTC Unix timestamp, and a random 32-hex nonce. Clock tolerance is two minutes. SQL stores used nonces; fresh time-bound proofs are required for retries. Nonce retention is ten minutes, longer than the replay-validity window. Certificate issuance is serialized per device.

Only registered, enabled devices with enabled customers may enroll. Before confirmation a retry with the same key returns the same certificate; changing the key requires operator rotation. After storing the certificate, the agent signs `UIC-TMS-IOT-CONFIRM-v1` + newline + the uppercase certificate SHA-256 using SHA256withRSA and calls `/device-iot/confirm`. Confirmation is idempotent and marks the certificate **Confirmed**, proving that the terminal possesses the private key while leaving recovery open if MQTT cannot yet connect. The first MQTT admission that successfully validates both the confirmed client certificate and the device's unique password atomically marks the certificate **Provisioned**, records the audit event and disables subsequent credential/certificate downloads. Issued certificates cannot establish MQTT sessions.

## Lifecycle and operator controls

Open **Device > Operations > Credential management > IoT certificates**. Customer scope controls read access; SuperAdmin controls rotation and revocation, with the portal confirmation dialog. The adjacent Device Secret action rotates the separate per-device credential, not the IoT certificate. See the bilingual manuals for recovery steps.

- **Issued**: signed certificate delivered; possession acknowledgement outstanding.
- **Confirmed**: private-key possession verified; MQTT connection is still pending and provisioning can be retried.
- **Provisioned**: the first authenticated MQTT connection succeeded and further credential/certificate downloads are locked.
- **Rotate**: revoke every previous non-revoked certificate, clear online presence and permit issuance of a replacement. On retry, the agent discards the old key and generates a new key/certificate.
- **Revoke**: revoke existing certificates and block all further certificate downloads. Only explicit rotation reopens provisioning.

Revoked rows retain DER certificate, fingerprint, device binding, expiry, revoked time and reason. They are never reused or deleted during rotation. The database is the authoritative revocation registry; this implementation does not publish an X.509 CRL or OCSP service. The broker checks the custom CA chain and SQL state during TLS authentication, checks the device/fingerprint binding during MQTT authentication, and rechecks authorization for device traffic and during its dispatch loop. Existing sessions are disconnected when revocation is observed (normally within the next one-second loop; service failures/latency may delay physical disconnect). Data-service failure denies new TLS admissions and prevents authorized operations; no cached allow fallback is used.

## Server installation and migration

ServerSetup generates a distinct 10-year IoT CA on first installation, preserves it on updates, and requires the administrator to enter the common bootstrap key through a masked prompt. CA PFX/password and bootstrap key must be backed up securely. Production passwords/key are protected by DPAPI and restricted ACLs. Development `.local` configuration contains test secrets and must not be distributed. The server package contains no CA private keys or bootstrap secrets.

Upgrading the broker is a mandatory mutual-TLS change: older agents without device certificates will no longer connect. Provision the common key and unique device enrollment, install UIC Home 3.9.14-dev or the current xTMSAgent, and verify each device reaches Provisioned before completing rollout. There is no password-only compatibility listener. Losing a terminal key requires operator rotation. Back up database and CA together; stale revocation data can restore obsolete authorization. CA rotation/multiple concurrent issuers and automatic renewal are not implemented.

## Validation

`dotnet run --project tests/TMS.IotTests` uses the isolated UICConnectTMS_IotTest database to exercise stale/future proofs, serial/key validation, replay rejection, retry delivery, possession acknowledgement, recovery before connection, connection-gated download lock, foreign/revoked certificates, authorization and retained history. The main integration suite provisions a test certificate and checks password-only MQTT rejection before testing the authenticated MQTT flow.

Android TLS requires the Keystore key to allow the digest/padding operations used internally by Conscrypt; see [Android KeyGenParameterSpec documentation](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder). This does not export the key or disable TLS validation.

Verified on NEWPOS NEW9810, NEXGO N82 and UIC A10-T: first-connection provisioning lock, MQTT reconnection and fresh telemetry. NEWPOS rotation retains revoked history. Current clean integration validation passes 239 checks in addition to dedicated IoT lifecycle checks, including connection-gated provisioning, revocation and reconnect denial.
