Files
ulweb/Secure MQTT.md
ulhub 842cb23e1f Add device-certificate mTLS auth, live position tracking, and API docs
Introduces a CA/PKI module so field devices can authenticate to Mosquitto
over TLS (8883) with per-device client certificates (CN = serial number)
instead of a shared password, with matching Devices/MQTT-Certs UI. Adds
live transmitter position tracking alongside logged points, an MQTTS
transport option in the simulator for exercising the real cert-auth path,
and Swagger API docs at /api/docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 01:40:12 +00:00

142 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
There is **nothing fundamentally wrong** with the approach, provided the devices **private key never leaves the device**. The main complication is that the phone—not the BLE device—is establishing the MQTT/TLS connection.
## The key distinction
The certificate itself is public. Authentication occurs because the client proves possession of the corresponding **private key** by signing part of the TLS handshake. In TLS 1.3, this happens in the `CertificateVerify` step. ([IETF Datatracker][1])
Therefore, simply reading the certificate over BLE and giving it to the phone is insufficient. You need one of these architectures.
### 1. Device acts as a remote TLS signer
The phone performs the MQTT/TLS connection but delegates private-key operations to the BLE device:
```text
MQTT broker
↕ TLS
Phone MQTT/TLS client
↕ BLE signing request
Embedded device/private key
```
During the TLS handshake:
1. Phone sends the device certificate.
2. TLS library generates the `CertificateVerify` data.
3. Phone sends the digest/signing request over BLE.
4. Device signs it using its private key.
5. Device returns only the signature.
6. Phone completes mutual TLS.
This is cryptographically valid and is similar to using a secure element, smart card, TPM, or hardware-backed keystore. Hardware-backed key systems are specifically designed so applications can use a key without extracting it. ([Android Open Source Project][2])
The difficulty is implementation: your Android/iOS MQTT and TLS libraries must support an **external or asynchronous private-key signer**. Many high-level MQTT libraries expect a conventional `PrivateKey` object and do not readily support a BLE-backed signing operation.
### 2. Device signs an application-level authentication challenge
This is usually easier and often cleaner:
```text
Phone → server: Request device-authentication challenge
Server → phone: Random nonce
Phone → BLE device: Sign nonce + context
Device → phone: Signature
Phone → server: Certificate + signature
Server → phone: Short-lived MQTT credential
Phone → MQTT broker: Connect using short-lived credential
```
The short-lived MQTT credential could be:
* A JWT or similar access token
* A temporary username/password
* A temporary MQTT client certificate
* A session credential issued by your backend
I would generally favor this architecture. It lets your backend authenticate the device certificate while the phone uses an ordinary MQTT client library.
Include at least this in the signed data:
```text
protocol_version
device_serial_number
server_nonce
intended_server/audience
timestamp or expiration
requested permissions
phone/app session identifier
```
That prevents the signature from being reused for another server, session, or purpose.
## Principal security concerns
### Do not export the private key
Sending both the certificate and private key to the phone eliminates much of the value of provisioning a unique device credential. A compromised or rooted phone could copy the identity permanently.
Only send signatures from the device.
### Secure the BLE connection
BLE should use authenticated LE Secure Connections, preferably Numeric Comparison, Passkey Entry, or an equivalent product-specific enrollment process. “Just Works” pairing encrypts traffic but does not provide meaningful protection against an active man-in-the-middle during pairing. ([Nordic Developer Academy][3])
For stronger protection, add application-layer security and mutual device/app authentication rather than relying solely on BLE pairing.
### Avoid creating an unrestricted signing oracle
Do not expose a generic BLE command such as:
```text
SIGN arbitrary_bytes
```
A malicious phone application could use the device to sign unrelated material.
Instead expose a narrowly scoped operation:
```text
AUTHENTICATE_MQTT(challenge, broker_id, expiration)
```
The firmware should:
* Validate the request format
* Require an approved broker or backend identifier
* Apply a domain-separation prefix such as `MYPRODUCT-MQTT-AUTH-V1`
* Reject stale challenges
* Rate-limit attempts
* Optionally require that the phone was previously provisioned or bonded
### Broker authorization remains necessary
A valid certificate should identify the device, but it should not automatically grant access to every MQTT topic. Use broker ACLs tied to the authenticated device identity, for example:
```text
devices/DEVICE123/telemetry publish
devices/DEVICE123/commands subscribe
```
MQTT itself recommends using authentication, authorization, and secure communication mechanisms for sensitive systems. ([OASIS Open][4])
## My recommendation
For a phone acting as a passive BLE-to-cloud data ferry, I would use:
```text
Permanent private key: embedded device only
Device certificate: embedded device, readable by phone
Device authentication: signed server challenge over BLE
Phone MQTT credential: short-lived token or temporary certificate
MQTT authorization: device-specific topic ACL
BLE protection: authenticated pairing plus app-level binding
```
That preserves the devices permanent identity, works with normal phone MQTT libraries, supports credential expiration and revocation, and prevents the permanent private key from ever reaching the phone.
Direct BLE-backed TLS signing is also sound, but it is substantially harder to integrate and may tie you to a particular TLS/MQTT implementation.
[1]: https://datatracker.ietf.org/doc/html/rfc8446?utm_source=chatgpt.com "The Transport Layer Security (TLS) Protocol Version 1.3"
[2]: https://source.android.com/docs/security/features/keystore?utm_source=chatgpt.com "Hardware-backed Keystore"
[3]: https://academy.nordicsemi.com/courses/bluetooth-low-energy-fundamentals/lessons/lesson-5-bluetooth-le-security-fundamentals/topic/security-modes/?utm_source=chatgpt.com "Security modes - Nordic Developer Academy"
[4]: https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html?utm_source=chatgpt.com "MQTT Version 5.0 | OASIS Standard"