🚧 Update certificate management

This commit is contained in:
Jonas Bengtsson
2026-09-19 17:12:58 +02:00
parent 111d6cc390
commit a16a01b1e7
21 changed files with 1128 additions and 37 deletions
+63 -1
View File
@@ -220,4 +220,66 @@ def fingerprint(certificate_pem: str) -> str:
"""SHA-256 fingerprint of a certificate, colon-separated hex."""
cert = x509.load_pem_x509_certificate(certificate_pem.encode("utf-8"))
digest = cert.fingerprint(hashes.SHA256())
return ":".join(f"{b:02x}" for b in digest)
return ":".join(f"{b:02x}" for b in digest)
def issue_client_certificate(
*,
ca_certificate_pem: str,
ca_private_key_pem: str,
common_name: str,
validity_days: int,
) -> IssuedPair:
"""Issue a client certificate signed by the given CA (spec section 20).
The CN MUST equal the MQTT username of the dynsec client this
certificate authenticates: with use_identity_as_username the broker
maps CN -> MQTT identity (spec section 24), so a mismatch means the
certificate authenticates as a username that has no dynsec client.
clientAuth EKU, no SANs — device identity only.
"""
if not common_name.strip():
raise PkiError("common name must not be empty")
ca_cert = x509.load_pem_x509_certificate(ca_certificate_pem.encode("utf-8"))
ca_key = serialization.load_pem_private_key(
ca_private_key_pem.encode("utf-8"), password=None
)
# Type narrowing for the checker: certificate signing requires an
# RSA/EC/Ed25519/Ed448 key — DH/DSA keys load but cannot sign certs.
if not isinstance(
ca_key,
(
rsa.RSAPrivateKey,
ec.EllipticCurvePrivateKey,
ed25519.Ed25519PrivateKey,
ed448.Ed448PrivateKey,
),
):
raise PkiError("the CA private key type cannot sign certificates")
key = _generate_key("ecdsa-p256")
now = datetime.datetime.now(datetime.UTC)
subject = _name(common_name, None, None, None)
cert = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(ca_cert.subject)
.public_key(key.public_key())
.serial_number(_serial())
.not_valid_before(now - datetime.timedelta(minutes=5))
.not_valid_after(_not_after(now, validity_days))
.add_extension(
x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]),
critical=False,
)
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
.sign(ca_key, hashes.SHA256())
)
return IssuedPair(
certificate_pem=_pem_cert(cert),
private_key_pem=_pem_key(key),
serial_number=cert.serial_number,
not_valid_after=cert.not_valid_after_utc,
)