🚧 Update certificate management
This commit is contained in:
+63
-1
@@ -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,
|
||||
)
|
||||
Reference in New Issue
Block a user