@classmethod
def create(cls, config: CertificateConfig) -> tuple[Self, KeyPair]:
"""Create a new certificate base and private key."""
_require_crypto()
try:
logger.debug("πππ CertificateBase.create: Starting base creation")
not_valid_before = config["not_valid_before"]
not_valid_after = config["not_valid_after"]
if not_valid_before.tzinfo is None:
not_valid_before = not_valid_before.replace(tzinfo=UTC)
if not_valid_after.tzinfo is None:
not_valid_after = not_valid_after.replace(tzinfo=UTC)
logger.debug(
"ππ
Certificate validity dates configured",
not_valid_before=not_valid_before.isoformat(),
not_valid_after=not_valid_after.isoformat(),
)
private_key: KeyPair
match config["key_type"]:
case KeyType.RSA:
key_size = config.get("key_size", DEFAULT_RSA_KEY_SIZE)
logger.debug(f"πππ Generating RSA key (size: {key_size})")
private_key = rsa.generate_private_key(public_exponent=65537, key_size=key_size)
case KeyType.ECDSA:
curve_choice = config.get("curve", CurveType.SECP384R1)
logger.debug(f"πππ Generating ECDSA key (curve: {curve_choice})")
curve = getattr(ec, curve_choice.name)()
private_key = ec.generate_private_key(curve)
case _:
raise ValueError(f"Internal Error: Unsupported key type: {config['key_type']}")
subject = cls._create_name(config["common_name"], config["organization"])
issuer = cls._create_name(config["common_name"], config["organization"])
serial_number = x509.random_serial_number()
base = cls(
subject=subject,
issuer=issuer,
public_key=private_key.public_key(),
not_valid_before=not_valid_before,
not_valid_after=not_valid_after,
serial_number=serial_number,
)
return base, private_key
except Exception as e:
logger.error(
f"πβ CertificateBase.create: Failed: {e}",
extra={"error": str(e), "trace": traceback.format_exc()},
)
raise CertificateError(f"Failed to generate certificate base: {e}") from e