Skip to content

Certificate

provide.foundation.crypto.certificates.certificate

TODO: Add module docstring.

Classes

Certificate

X.509 certificate management using attrs.

This class should be instantiated via factory methods: - Certificate.from_pem() - Load from PEM strings - Certificate.generate() - Generate new certificate - Certificate.create_ca() - Generate CA certificate - Certificate.create_signed_certificate() - Generate signed certificate - Certificate.create_self_signed_server_cert() - Generate self-signed server cert - Certificate.create_self_signed_client_cert() - Generate self-signed client cert

Attributes
is_ca property
is_ca: bool

Checks if the certificate has the Basic Constraints CA flag set to True.

is_valid cached property
is_valid: bool

Checks if the certificate is currently valid based on its dates.

issuer property
issuer: str

Returns the certificate issuer as an RFC4514 string.

public_key property
public_key: PublicKey | None

Returns the public key object from the certificate.

serial_number property
serial_number: int | None

Returns the certificate serial number.

subject property
subject: str

Returns the certificate subject as an RFC4514 string.

trust_chain property writable
trust_chain: list[Certificate]

Returns the list of trusted certificates associated with this one.

Functions
__eq__
__eq__(other: object) -> bool

Custom equality based on subject and serial number.

Source code in provide/foundation/crypto/certificates/certificate.py
def __eq__(self, other: object) -> bool:
    """Custom equality based on subject and serial number."""
    if not isinstance(other, Certificate):
        return NotImplemented
    if not hasattr(self, "_base") or not hasattr(other, "_base"):
        return False
    eq = (
        self._base.subject == other._base.subject and self._base.serial_number == other._base.serial_number
    )
    return eq
__hash__
__hash__() -> int

Custom hash based on subject and serial number.

Source code in provide/foundation/crypto/certificates/certificate.py
def __hash__(self) -> int:
    """Custom hash based on subject and serial number."""
    if not hasattr(self, "_base"):
        logger.warning("📜🔍⚠️ __hash__ called before _base initialized")
        return hash((None, None))

    h = hash((self._base.subject, self._base.serial_number))
    return h
create_ca classmethod
create_ca(
    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,
) -> Certificate

Creates a new self-signed CA certificate.

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def create_ca(
    cls,
    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,
) -> Certificate:
    """Creates a new self-signed CA certificate."""
    from provide.foundation.crypto.certificates.factory import create_ca_certificate

    return create_ca_certificate(
        common_name,
        organization_name,
        validity_days,
        key_type,
        key_size,
        ecdsa_curve,
    )
create_self_signed_client_cert classmethod
create_self_signed_client_cert(
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
) -> Certificate

Creates a new self-signed end-entity certificate suitable for a client.

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def create_self_signed_client_cert(
    cls,
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
) -> Certificate:
    """Creates a new self-signed end-entity certificate suitable for a client."""
    from provide.foundation.crypto.certificates.factory import (
        create_self_signed_client_cert,
    )

    return create_self_signed_client_cert(
        common_name,
        organization_name,
        validity_days,
        alt_names,
        key_type,
        key_size,
        ecdsa_curve,
    )
create_self_signed_server_cert classmethod
create_self_signed_server_cert(
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
) -> Certificate

Creates a new self-signed end-entity certificate suitable for a server.

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def create_self_signed_server_cert(
    cls,
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
) -> Certificate:
    """Creates a new self-signed end-entity certificate suitable for a server."""
    from provide.foundation.crypto.certificates.factory import (
        create_self_signed_server_cert,
    )

    return create_self_signed_server_cert(
        common_name,
        organization_name,
        validity_days,
        alt_names,
        key_type,
        key_size,
        ecdsa_curve,
    )
create_signed_certificate classmethod
create_signed_certificate(
    ca_certificate: Certificate,
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
    is_client_cert: bool = False,
) -> Certificate

Creates a new certificate signed by the provided CA certificate.

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def create_signed_certificate(
    cls,
    ca_certificate: Certificate,
    common_name: str,
    organization_name: str,
    validity_days: int,
    alt_names: list[str] | None = None,
    key_type: str = DEFAULT_CERTIFICATE_KEY_TYPE,
    key_size: int = DEFAULT_RSA_KEY_SIZE,
    ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
    is_client_cert: bool = False,
) -> Certificate:
    """Creates a new certificate signed by the provided CA certificate."""
    from provide.foundation.crypto.certificates.factory import (
        create_signed_certificate,
    )

    return create_signed_certificate(
        ca_certificate,
        common_name,
        organization_name,
        validity_days,
        alt_names,
        key_type,
        key_size,
        ecdsa_curve,
        is_client_cert,
    )
from_pem classmethod
from_pem(
    cert_pem: str, key_pem: str | None = None
) -> Certificate

Load certificate from PEM strings.

Parameters:

Name Type Description Default
cert_pem str

Certificate in PEM format (string or URI)

required
key_pem str | None

Optional private key in PEM format (string or URI)

None

Returns:

Type Description
Certificate

Certificate instance

Raises:

Type Description
CertificateError

If loading fails

Example

cert = Certificate.from_pem(cert_pem_string, key_pem_string) assert cert.is_valid

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def from_pem(cls, cert_pem: str, key_pem: str | None = None) -> Certificate:
    """Load certificate from PEM strings.

    Args:
        cert_pem: Certificate in PEM format (string or URI)
        key_pem: Optional private key in PEM format (string or URI)

    Returns:
        Certificate instance

    Raises:
        CertificateError: If loading fails

    Example:
        >>> cert = Certificate.from_pem(cert_pem_string, key_pem_string)
        >>> assert cert.is_valid
    """
    base, x509_cert, private_key, cert_pem_str, key_pem_str = load_certificate_from_pem(
        cert_pem,
        key_pem,
    )

    # Extract metadata from x509 cert subject
    try:
        cn_attr = x509_cert.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0]
        common_name = cn_attr.value
    except (IndexError, AttributeError):
        common_name = "Unknown"

    try:
        org_attr = x509_cert.subject.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATION_NAME)[0]
        organization_name = org_attr.value
    except (IndexError, AttributeError):
        organization_name = "Unknown"

    # Calculate validity days from certificate dates
    validity_delta = base.not_valid_after - base.not_valid_before
    validity_days = validity_delta.days

    # Determine key type from public key
    if isinstance(base.public_key, rsa.RSAPublicKey):
        key_type = "rsa"
    elif isinstance(base.public_key, ec.EllipticCurvePublicKey):
        key_type = "ecdsa"
    else:
        key_type = "unknown"

    return cls(
        _base=base,
        _cert=x509_cert,
        _private_key=private_key,
        cert_pem=cert_pem_str,
        key_pem=key_pem_str,
        common_name=common_name,
        organization_name=organization_name,
        validity_days=validity_days,
        key_type=key_type,
    )
generate classmethod
generate(
    common_name: str = DEFAULT_CERTIFICATE_COMMON_NAME,
    organization_name: str = DEFAULT_CERTIFICATE_ORGANIZATION_NAME,
    validity_days: int = DEFAULT_CERTIFICATE_VALIDITY_DAYS,
    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 = True,
) -> Certificate

Generate a new certificate with a new keypair.

Parameters:

Name Type Description Default
common_name str

Certificate common name

DEFAULT_CERTIFICATE_COMMON_NAME
organization_name str

Organization name

DEFAULT_CERTIFICATE_ORGANIZATION_NAME
validity_days int

Number of days certificate is valid

DEFAULT_CERTIFICATE_VALIDITY_DAYS
key_type str

Key type ("rsa" or "ecdsa")

DEFAULT_CERTIFICATE_KEY_TYPE
key_size int

RSA key size in bits

DEFAULT_RSA_KEY_SIZE
ecdsa_curve str

ECDSA curve name

DEFAULT_CERTIFICATE_CURVE
alt_names list[str] | None

Subject alternative names

None
is_ca bool

Whether this is a CA certificate

False
is_client_cert bool

Whether this is a client certificate

True

Returns:

Type Description
Certificate

New Certificate instance

Example

cert = Certificate.generate( ... common_name="example.com", ... organization_name="Example Corp", ... ) assert cert._private_key is not None

Source code in provide/foundation/crypto/certificates/certificate.py
@classmethod
def generate(
    cls,
    common_name: str = DEFAULT_CERTIFICATE_COMMON_NAME,
    organization_name: str = DEFAULT_CERTIFICATE_ORGANIZATION_NAME,
    validity_days: int = DEFAULT_CERTIFICATE_VALIDITY_DAYS,
    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 = True,
) -> Certificate:
    """Generate a new certificate with a new keypair.

    Args:
        common_name: Certificate common name
        organization_name: Organization name
        validity_days: Number of days certificate is valid
        key_type: Key type ("rsa" or "ecdsa")
        key_size: RSA key size in bits
        ecdsa_curve: ECDSA curve name
        alt_names: Subject alternative names
        is_ca: Whether this is a CA certificate
        is_client_cert: Whether this is a client certificate

    Returns:
        New Certificate instance

    Example:
        >>> cert = Certificate.generate(
        ...     common_name="example.com",
        ...     organization_name="Example Corp",
        ... )
        >>> assert cert._private_key is not None
    """
    alt_names = alt_names or default_certificate_alt_names()

    base, x509_cert, private_key, cert_pem, key_pem = generate_certificate(
        common_name=common_name,
        organization_name=organization_name,
        validity_days=validity_days,
        key_type=key_type,
        key_size=key_size,
        ecdsa_curve=ecdsa_curve,
        alt_names=alt_names,
        is_ca=is_ca,
        is_client_cert=is_client_cert,
    )

    return cls(
        _base=base,
        _cert=x509_cert,
        _private_key=private_key,
        cert_pem=cert_pem,
        key_pem=key_pem,
        common_name=common_name,
        organization_name=organization_name,
        validity_days=validity_days,
        key_type=key_type,
        key_size=key_size,
        ecdsa_curve=ecdsa_curve,
        alt_names=alt_names,
    )
verify_trust
verify_trust(other_cert: Self) -> bool

Verifies if the other_cert is trusted based on this certificate's trust chain.

Source code in provide/foundation/crypto/certificates/certificate.py
def verify_trust(self, other_cert: Self) -> bool:
    """Verifies if the `other_cert` is trusted based on this certificate's trust chain."""
    return verify_trust_impl(self, other_cert, self._trust_chain)

Functions