def generate_certificate(
common_name: str,
organization_name: str,
validity_days: int,
key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
key_size: int = DEFAULT_RSA_KEY_SIZE,
ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
alt_names: list[str] | None = None,
is_ca: bool = False,
is_client_cert: bool = False,
) -> tuple[
CertificateBase,
x509.Certificate,
KeyPair,
str,
str,
]:
"""Generate a new certificate with a keypair.
Returns:
Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
"""
try:
logger.debug("πππ Generating new keypair")
# Calculate validity period
now = datetime.now(UTC)
not_valid_before = now - timedelta(days=1)
not_valid_after = now + timedelta(days=validity_days)
# Parse and validate key type parameters
gen_key_type, gen_key_size, gen_curve = _parse_key_type_and_params(key_type, key_size, ecdsa_curve)
# Build certificate configuration
conf = _build_certificate_config(
common_name=common_name,
organization_name=organization_name,
not_valid_before=not_valid_before,
not_valid_after=not_valid_after,
alt_names=alt_names,
gen_key_type=gen_key_type,
gen_key_size=gen_key_size,
gen_curve=gen_curve,
)
logger.debug(f"πππ Generation config: {conf}")
# Generate base certificate and private key
base, private_key = CertificateBase.create(conf)
# Create X.509 certificate
x509_cert = create_x509_certificate(
base=base,
private_key=private_key,
alt_names=alt_names or ["localhost"],
is_ca=is_ca,
is_client_cert=is_client_cert,
)
if x509_cert is None:
raise CertificateError("Certificate object (_cert) is None after creation")
# Serialize to PEM format
cert_pem, key_pem = _serialize_to_pem(x509_cert, private_key)
return base, x509_cert, private_key, cert_pem, key_pem
except Exception as e:
logger.error(
f"πβ Failed to generate certificate. Error: {type(e).__name__}: {e}",
extra={"error": str(e), "trace": traceback.format_exc()},
)
raise CertificateError(
f"Failed to initialize certificate. Original error: {type(e).__name__}: {e}"
) from e