🚧 Add CA and server cert management

This commit is contained in:
Jonas Bengtsson
2026-09-19 13:35:49 +02:00
parent 45de68f0b7
commit 111d6cc390
20 changed files with 1317 additions and 34 deletions
+103 -1
View File
@@ -277,4 +277,106 @@ def test_audit_events_recorded(admin_client, db_session):
admin_client.post("/api/v1/mqtt/clients", json={"username": "audited-1"})
actions = db_session.scalars(select(AuditEvent.action)).all()
assert "CLIENT_CREATED" in actions
assert "CLIENT_CREATED" in actions
# --- PKI: CA + server certificates (spec sections 16-19) -----------------------
def test_ca_creation_and_inventory(admin_client):
response = admin_client.post(
"/api/v1/pki/ca",
json={
"common_name": "MQTT Platform Root CA",
"validity_days": 3650,
"key_algorithm": "ecdsa-p256",
},
)
assert response.status_code == 201, response.text
ca = response.json()
assert ca["is_ca"] is True
assert ca["status"] == "issued"
assert len(ca["serial_number"]) > 10
assert ca["fingerprint"].count(":") == 31 # sha-256 hex pairs
# No private key material ever appears in a response.
assert "private_key_pem" not in ca
assert "BEGIN PRIVATE KEY" not in response.text
cas = admin_client.get("/api/v1/pki/ca").json()
assert any(c["common_name"] == "MQTT Platform Root CA" for c in cas)
def test_server_certificate_issuance(admin_client):
ca_id = admin_client.post(
"/api/v1/pki/ca",
json={
"common_name": "Test CA",
"validity_days": 3650,
"key_algorithm": "ecdsa-p256",
},
).json()["id"]
response = admin_client.post(
"/api/v1/pki/certificates",
json={
"ca_id": ca_id,
"common_name": "mqtt.example.local",
"validity_days": 365,
"dns_sans": ["mqtt", "mqtt.local"],
"ip_sans": ["10.20.30.40"],
},
)
assert response.status_code == 201, response.text
cert = response.json()
assert cert["is_ca"] is False
assert cert["parent_id"] == ca_id
assert cert["dns_sans"] == ["mqtt", "mqtt.local"]
assert cert["ip_sans"] == ["10.20.30.40"]
assert "BEGIN PRIVATE KEY" not in response.text
# The download endpoint returns the public PEM.
download = admin_client.get(f"/api/v1/pki/certificates/{cert['id']}/download")
assert download.status_code == 200
assert "BEGIN CERTIFICATE" in download.text
assert "PRIVATE KEY" not in download.text
def test_server_certificate_is_cryptographically_valid(admin_client):
"""The issued chain must verify against the issuing CA (real crypto)."""
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import ec
ca_id = admin_client.post(
"/api/v1/pki/ca",
json={
"common_name": "Verify CA",
"validity_days": 3650,
"key_algorithm": "ecdsa-p256",
},
).json()["id"]
cert_id = admin_client.post(
"/api/v1/pki/certificates",
json={"ca_id": ca_id, "common_name": "broker.local", "validity_days": 30},
).json()["id"]
ca_pem = admin_client.get(f"/api/v1/pki/certificates/{ca_id}/download").text
cert_pem = admin_client.get(f"/api/v1/pki/certificates/{cert_id}/download").text
ca_cert = x509.load_pem_x509_certificate(ca_pem.encode())
cert = x509.load_pem_x509_certificate(cert_pem.encode())
# Signature verification: the leaf must validate against the CA key.
ca_cert.public_key().verify(
cert.signature,
cert.tbs_certificate_bytes,
ec.ECDSA(cert.signature_hash_algorithm),
)
# Serial and fingerprint match the inventory fields.
inv = next(
c
for c in admin_client.get("/api/v1/pki/certificates").json()
if c["id"] == cert_id
)
assert int(inv["serial_number"]) == cert.serial_number
from app.pki import fingerprint as pki_fingerprint
assert inv["fingerprint"] == pki_fingerprint(cert_pem)