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>
142 lines
5.9 KiB
Markdown
142 lines
5.9 KiB
Markdown
There is **nothing fundamentally wrong** with the approach, provided the device’s **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 device’s 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"
|