2025-08-09 18:52:35 +00:00
|
|
|
# Authentication Using Signed Certificates
|
|
|
|
|
|
|
|
Using client-specific certificates, signed by a common authority (even if self-signed) provides
|
|
|
|
a highly secure way of authenticating mqtt clients. Often used with IoT devices where a unique
|
|
|
|
certificate can be initialized on initial provisioning.
|
|
|
|
|
|
|
|
With so many options, X509 certificates can be daunting to create with `openssl`. Included are
|
|
|
|
command line utilities to generate a root self-signed certificate and then the proper broker and
|
|
|
|
device certificates with the correct X509 attributes to enable authenticity.
|
|
|
|
|
|
|
|
### Quick start
|
|
|
|
|
|
|
|
Generate a self-signed root credentials and server credentials:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
$ ca_creds --country US --state NY --locality NY --org-name "My Org's Name" --cn "my.domain.name"
|
|
|
|
$ server_creds --country US --org-name "My Org's Name" --cn "my.domain.name"
|
|
|
|
```
|
|
|
|
|
|
|
|
!!! warning "Security of private keys"
|
|
|
|
Your root credential private key and your server key should *never* be shared with anyone. The
|
|
|
|
certificates -- specifically the root CA certificate -- is completely safe to share and will need
|
|
|
|
to be shared along with device credentials when using a self-signed CA.
|
|
|
|
|
|
|
|
Include in your server config:
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
listeners:
|
|
|
|
ssl-mqtt:
|
|
|
|
bind: "127.0.0.1:8883"
|
|
|
|
ssl: true
|
|
|
|
certfile: server.crt
|
|
|
|
keyfile: server.key
|
|
|
|
cafile: ca.crt
|
|
|
|
plugins:
|
|
|
|
amqtt.contrib.cert.CertificateAuthPlugin:
|
|
|
|
uri_domain: my.domain.name
|
|
|
|
```
|
|
|
|
|
|
|
|
Generate a device's credentials:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
$ device_creds --country US --org-name "My Org's Name" --device-id myUniqueDeviceId --uri my.domain.name
|
|
|
|
```
|
|
|
|
|
|
|
|
And use these to initialize the `MQTTClient`:
|
|
|
|
|
|
|
|
```python
|
|
|
|
import asyncio
|
|
|
|
from amqtt.client import MQTTClient
|
|
|
|
|
|
|
|
client_config = {
|
|
|
|
'keyfile': 'myUniqueDeviceId.key',
|
|
|
|
'certfile': 'myUniqueDeviceId.crt',
|
|
|
|
'broker': {
|
|
|
|
'cafile': 'ca.crt'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async def main():
|
|
|
|
client = MQTTClient(config=client_config)
|
|
|
|
await client.connect("mqtts://my.domain.name:8883")
|
|
|
|
# publish messages or subscribe to receive
|
|
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
## Background
|
2025-08-09 18:52:35 +00:00
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
Often used for IoT devices, this method provides the most secure form of identification. A root
|
|
|
|
certificate, often referenced as a CA certificate -- either issued by a known authority (such as LetsEncrypt)
|
|
|
|
or a self-signed certificate) is used to sign a private key and certificate for the server. Each device/client
|
|
|
|
also gets a unique private key and certificate signed by the same CA certificate; also included in the device
|
|
|
|
certificate is a 'SAN' or SubjectAlternativeName which is the device's unique identifier.
|
|
|
|
|
|
|
|
Since both server and device certificates are signed by the same CA certificate, the client can
|
|
|
|
verify the server's authenticity; and the server can verify the client's authenticity. And since
|
|
|
|
the device's certificate contains a x509 SAN, the server (with this plugin) can identify the device securely.
|
|
|
|
|
|
|
|
!!! note "URI and Client ID configuration"
|
|
|
|
`uri_domain` configuration must be set to the same uri used to generate the device credentials
|
|
|
|
|
|
|
|
when a device is connecting with private key and certificate, the `client_id` must
|
|
|
|
match the device id used to generate the device credentials.
|
|
|
|
|
|
|
|
Available ore three scripts to help with the key generation and certificate signing: `ca_creds`, `server_creds`
|
|
|
|
and `device_creds`.
|
|
|
|
|
|
|
|
!!! note "Configuring broker & client for using Self-signed root CA"
|
|
|
|
If using self-signed root credentials, the `cafile` configuration for both broker and client need to be
|
|
|
|
configured with `cafile` set to the `ca.crt`.
|
|
|
|
|
|
|
|
## Root & Certificate Credentials
|
2025-08-09 18:52:35 +00:00
|
|
|
|
|
|
|
The process for generating a server's private key and certificate is only done once. If you have a private key & certificate --
|
|
|
|
such as one from verifying your webserver's domain with LetsEncrypt -- that you want to use, pass them to the `server_creds` cli.
|
|
|
|
If you'd like to use a self-signed certificate, generate your own CA by running the `ca_creds` cli (make sure your client is
|
|
|
|
configured with `check_hostname` as `False`).
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
---
|
|
|
|
config:
|
|
|
|
theme: redux
|
|
|
|
---
|
|
|
|
flowchart LR
|
|
|
|
subgraph ca_cred["ca_cred #40;cli#41; or other CA"]
|
|
|
|
ca["ca key & cert"]
|
|
|
|
end
|
|
|
|
|
|
|
|
subgraph server_cred["server_cred fl°°40¶ßclifl°°41¶ß"]
|
|
|
|
scsr("certificate signing<br>request fl°°40¶ßCSRfl°°41¶ß with<br>SAN of DNS & IP Address")
|
|
|
|
spk["private key"]
|
|
|
|
ssi["sign csr"]
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
spk -.-> skc["server key & cert"]
|
|
|
|
ca_cred --> ssi
|
|
|
|
spk --> scsr
|
|
|
|
con["country, org<br>& common name"] --> scsr
|
|
|
|
scsr --> ssi
|
|
|
|
ssi --> skc
|
|
|
|
```
|
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
## Device credentials
|
2025-08-09 18:52:35 +00:00
|
|
|
|
|
|
|
For each device, create a device id to generate a device-specific private key and certificate using the `device_creds` cli.
|
|
|
|
Use the same CA as was used for the server (above) so the client & server recognize each other.
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
---
|
|
|
|
config:
|
|
|
|
theme: redux
|
|
|
|
---
|
|
|
|
flowchart LR
|
|
|
|
subgraph ca_cred["ca_cred #40;cli#41; or other CA"]
|
|
|
|
ca["ca key & cert"]
|
|
|
|
end
|
|
|
|
subgraph device_cred["device_cred fl°°40¶ßclifl°°41¶ß"]
|
|
|
|
ccsr("certificate signing<br>request fl°°40¶ßCSRfl°°41¶ß with<br>SAN of URI & DNS")
|
|
|
|
cpk["private key"]
|
|
|
|
csi["sign csr"]
|
|
|
|
end
|
|
|
|
cpk --> ccsr
|
|
|
|
csi --> ckc[device key & cert]
|
|
|
|
cpk -.-> ckc
|
|
|
|
ccsr --> csi
|
|
|
|
ca_cred --> csi
|
|
|
|
|
|
|
|
con["country, org<br/>common name<br/>& device id"] --> ccsr
|
|
|
|
```
|
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
## Configuration
|
2025-08-09 18:52:35 +00:00
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
::: amqtt.contrib.cert.UserAuthCertPlugin.Config
|
2025-08-09 18:52:35 +00:00
|
|
|
options:
|
|
|
|
show_source: false
|
|
|
|
heading_level: 4
|
|
|
|
extra:
|
|
|
|
class_style: "simple"
|
|
|
|
|
|
|
|
|
2025-08-11 21:38:50 +00:00
|
|
|
## Key and Certificate Generation
|
2025-08-09 18:52:35 +00:00
|
|
|
|
|
|
|
::: mkdocs-typer2
|
|
|
|
:module: amqtt.scripts.ca_creds
|
|
|
|
:name: ca_creds
|
|
|
|
|
|
|
|
::: mkdocs-typer2
|
|
|
|
:module: amqtt.scripts.server_creds
|
|
|
|
:name: server_creds
|
|
|
|
|
|
|
|
::: mkdocs-typer2
|
|
|
|
:module: amqtt.scripts.device_creds
|
|
|
|
:name: device_creds
|