cnert

Class cnert.CA

CA

CA(
    subject_attrs: NameAttrs | None = None,
    issuer_attrs: NameAttrs | None = None,
    path_length: int = 9,
    not_valid_before: datetime | None = None,
    not_valid_after: datetime | None = None,
    serial_number: int | None = None,
    parent: CA | None = None,
    intermediate_num: int = 0,
    signature_hash: HashAlgorithm
    | str
    | _UnsetType
    | None = _UNSET,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    private_key: CertificateIssuerPrivateKeyTypes
    | None = None,
    builtin_extensions: bool = True,
)

A CA object.

Examples: >>> ca = cnert.CA() >>> ca.is_root_ca True >>> ca.is_intermediate_ca False >>> ca.parent is None True

Parameters: subject_attrs: Subject Name Attributes. subject_attrs: Issuer Name Attributes. path_length: Maximum path length certificates subordinate. not_valid_before: CA not valid before date. not_valid_after: CA not valid after date. parent: Parent of CA. intermediate_num: Number of intermediates. signature_hash: Signature hash algorithm for the CA's own certificate. Defaults to SHA-256, or to None for an Edwards key, which takes no hash. extensions: Extra (extension, critical) pairs for the CA's own certificate. One that collides with a built-in extension replaces it wholesale. private_key: Private key for the CA's own certificate; generated when omitted. Reusing one key across a chain is allowed, and is how a key-reuse fixture is built. builtin_extensions: Add cnert's own extensions to the CA's own certificate. When false, only the supplied pairs are added. An authority without basic constraints is not a usable authority, which is deliberate.

Methods:

Attributes:
  • is_intermediate_ca (bool) –

    Returns:

  • is_root_ca (bool) –

    Examples:

is_intermediate_ca property
is_intermediate_ca: bool

Returns: Whether CA is a intermediate CA or not.

is_root_ca property
is_root_ca: bool

Examples: >>> ca = CA() >>> ca.is_root_ca True >>> intermediate = ca.issue_intermediate() >>> intermediate.is_root_ca False

Returns: Whether CA is a root CA or not.

issue_cert
issue_cert(
    *sans: str,
    subject_attrs: NameAttrs | None = None,
    not_valid_before: datetime | None = None,
    not_valid_after: datetime | None = None,
    serial_number: int | None = None,
    csr: CSR | None = None,
    signature_hash: HashAlgorithm
    | str
    | _UnsetType
    | None = _UNSET,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    private_key: CertificateIssuerPrivateKeyTypes
    | None = None,
    builtin_extensions: bool = True,
) -> Cert

Issues a certificate

Examples: >>> ca = CA() >>> ca.issue_cert()

Parameters: sans: Subject Alternative Names as positional arguments. subject_attrs: Subject Name Attributes. not_valid_before: Certificate not valid before date. not_valid_after: Certificate not valid after date. csr: A CSR object. signature_hash: Signature hash algorithm. Defaults to SHA-256, or to None when this CA's key is an Edwards key, which takes no hash. extensions: Extra (extension, critical) pairs. One that collides with a built-in extension replaces it wholesale. private_key: Private key for the certificate; generated when omitted. It is the certificate's subject key only: the signature always comes from this CA's key. A csr carries its own key, so the two are mutually exclusive. builtin_extensions: Add cnert's own extensions. When false, only the supplied pairs are added, subject alternative names included. The result is not a valid certificate for real use, which is the point: it is how a bare certificate is built.

Raises: ValueError: If both private_key and csr are given.

Returns: A Cert object.

issue_intermediate
issue_intermediate(
    subject_attrs: NameAttrs | None = None,
    not_valid_before: datetime | None = None,
    not_valid_after: datetime | None = None,
    serial_number: int | None = None,
    signature_hash: HashAlgorithm
    | str
    | _UnsetType
    | None = _UNSET,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    private_key: CertificateIssuerPrivateKeyTypes
    | None = None,
    builtin_extensions: bool = True,
) -> CA

Issues an intermediate CA.

Parameters: subject_attrs: Subject Name Attributes. not_valid_before: Intermediate not valid before date. not_valid_after: Intermediate not valid after date. serial_number: Serial number. signature_hash: Signature hash algorithm for the intermediate's certificate. extensions: Extra (extension, critical) pairs. private_key: Private key for the intermediate; generated when omitted. The intermediate is still signed by this CA's key. builtin_extensions: Add cnert's own extensions. When false, only the supplied pairs are added.

Raises: ValueError: If this CA's path length is 0.

Returns: A CA object.

Class cnert.NameAttrs

Instances are frozen and hashable; comparing against a non-NameAttrs object returns False instead of raising.

A value may be a sequence, which emits one attribute per value in the order given. That is how a distinguished name repeats an attribute type, which a lossless DN parse has to be tested against:

import cnert

subject = cnert.NameAttrs(
    COMMON_NAME="example.com", ORGANIZATIONAL_UNIT_NAME=["OU-A", "OU-B"]
)

Each value becomes its own relative distinguished name. The plus-joined OU=A+OU=B form, one relative distinguished name holding both, is not reachable this way. A multi-valued attribute reads back as a tuple. Note that rfc4514_string() prints most-specific first, so it renders the name above with OU-B before OU-A; the attribute order is still the one given.

Anything that is not a NameAttrs passed as subject_attrs or issuer_attrs raises TypeError.

NameAttrs

NameAttrs(**kwargs: str | Sequence[str])

Bases: Freezer

flowchart TD cnert.NameAttrs[NameAttrs] cnert.Freezer[Freezer] cnert.Freezer --> cnert.NameAttrs click cnert.NameAttrs href "" "cnert.NameAttrs" click cnert.Freezer href "" "cnert.Freezer"

An object for storing (and freezing) Name Attributes for Subject Name Attributes and Issuer Name Attributes.

Accepts any valid x509.NameAttribute as key arguments with arbitrary string values.

Has methods for returning initialized attributes in a dict and for returning a cryptography.x509.Name

There is alse a method for showing the allowed attributes.

Examples: >>> subject_attrs = cnert.NameAttrs(COMMON_NAME="example.com") >>> subject_attrs.COMMON_NAME 'example.com' >>> subject_attrs.dict_ {'COMMON_NAME': 'example.com'} >>> subject_attrs.x509_name()

    [`allowed_keys()`][cnert.NameAttrs.allowed_keys],
    each with a value or a sequence of values. A sequence
    emits one attribute per value in the order given, so
    `ORGANIZATIONAL_UNIT_NAME=["A", "B"]` renders as
    `OU=A,OU=B`. Attribute types are ordered
    alphabetically; only values within one type follow
    the caller.

Methods:

allowed_keys
allowed_keys() -> list[str]

Returns a list of allowed key arguments.

Examples: >>> cnert.NameAttrs().allowed_keys() ['BUSINESS_CATEGORY', 'COMMON_NAME', 'COUNTRY_NAME', 'DN_QUALIFIER', 'DOMAIN_COMPONENT', 'EMAIL_ADDRESS', 'GENERATION_QUALIFIER', 'GIVEN_NAME', 'INN', 'JURISDICTION_COUNTRY_NAME', 'JURISDICTION_LOCALITY_NAME', 'JURISDICTION_STATE_OR_PROVINCE_NAME', 'LOCALITY_NAME', 'OGRN', 'ORGANIZATIONAL_UNIT_NAME', 'ORGANIZATION_NAME', 'POSTAL_ADDRESS', 'POSTAL_CODE', 'PSEUDONYM', 'SERIAL_NUMBER', 'SNILS', 'STATE_OR_PROVINCE_NAME', 'STREET_ADDRESS', 'SURNAME', 'TITLE', 'UNSTRUCTURED_NAME', 'USER_ID', 'X500_UNIQUE_IDENTIFIER']

Returns: A list of valid key attributes.

x509_name
x509_name() -> Name

Examples: >>> subject_attrs = cnert.NameAttrs(COMMON_NAME="example.com") >>> subject_attrs.x509_name()

Returns: A cryptography.x509.Name

Class cnert._CertBuilder

_CertBuilder

_CertBuilder()

Builds and signs a X509 Certificate.

Methods:

  • build

    Does the Certificate building.

build
build(
    sans: tuple[str, ...],
    subject_attrs_X509_name: Name,
    issuer_attrs_X509_name: Name,
    serial_number: int,
    not_valid_before: datetime,
    not_valid_after: datetime,
    is_ca: bool,
    public_key: CertificateIssuerPublicKeyTypes,
    issuer_public_key: CertificateIssuerPublicKeyTypes
    | None = None,
    path_length: int | None = None,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    builtin_extensions: bool = True,
) -> None

Does the Certificate building.

Parameters: sans: Subject Alternative Names as positional arguments. subject_attrs_X509_name: Subject Attributes Names. issuer_attrs_X509_name: Issuer Atributes Names. serial_number: Serial number. not_valid_before: Not valid before date. not_valid_after: Note valid after date. is_ca: Add CA extension. public_key: Public key for the certificate. issuer_public_key: Issuer public key. path_length: Max path length. extensions: Extra (extension, critical) pairs. One that collides with a built-in extension replaces it. builtin_extensions: Add cnert's own extensions. When false, only the supplied pairs are added, subject alternative names included.

Class cnert.Cert

Cert was named _Cert through 0.10.x even though the public CA.issue_cert() returned it; the old name remains as a deprecated alias.

Cert

Cert(
    *sans: str,
    subject_attrs: NameAttrs,
    issuer_attrs: NameAttrs,
    not_valid_before: datetime | None = None,
    not_valid_after: datetime | None = None,
    serial_number: int | None = None,
    parent: Cert | None = None,
    private_key: CertificateIssuerPrivateKeyTypes
    | None = None,
    signature_hash: HashAlgorithm
    | str
    | _UnsetType
    | None = _UNSET,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    builtin_extensions: bool = True,
    path_length: int = 0,
    is_ca: bool = False,
)

A Cert object.

This object is returned by cnert.CA().issue_cert()

Examples:

>>> ca = CA()
>>> cert = ca.issue_cert()
>>> cert.subject_attrs
NameAttrs(COMMON_NAME="example.com")
>>> cert.issuer_attrs
NameAttrs(ORGANIZATION_NAME="Root CA")
>>> cert.not_valid_before
datetime.datetime(2023, 3, 24, 23, 56, 55, 901545)
>>> cert.not_valid_after
datetime.datetime(2023, 6, 23, 23, 56, 55, 901545)

Parameters: sans: Subject Alternative Names as positional arguments subject_attrs: Subject Name Attributes issuer_attrs: Issure Name Attributes not_valid_before: CA not valid before date not_valid_after: CA not valid after date serial_number: Serial number parent: Certificate of CA. private_key: Private key; generated when omitted. signature_hash: Signature hash algorithm. Defaults to SHA-256, or to None when the signing key is an Edwards key, which takes no hash. extensions: Extra (extension, critical) pairs. One that collides with a built-in extension replaces it wholesale. builtin_extensions: Add cnert's own extensions. When false, only the supplied pairs are added, subject alternative names included. The result is not a valid certificate for real use, which is the point: it is how a bare certificate is built. path_length: Path length is_ca: if CA

Raises: ValueError: If signature_hash does not suit the signing key. See cnert.build_private_key.

Attributes:
  • MD5 (str) –

    Examples:

  • SHA1 (str) –

    Examples:

  • SHA256 (str) –

    Examples:

  • authority_key_identifier_digest (str | None) –

    Examples:

  • private_key_pem_PKCS1 (bytes) –

    Examples:

  • private_key_pem_PKCS8 (bytes) –

    Examples:

  • public_key (CertificateIssuerPublicKeyTypes) –

    Examples:

  • public_key_pem (bytes) –

    Examples:

  • subject_key_identifier_digest (str) –

    Examples:

MD5 property
MD5: str

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.MD5 'A03D37486DD47BE3E9C7EC1624073856'

Returns: MD5 Fingerprint string in hexadecimal and upper case.

SHA1 property
SHA1: str

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.SHA1 '9E0A06CFB37B352FDA5B2226E6D631CF07D5D185'

Returns: SHA1 Fingerprint string in hexadecimal and upper case.

SHA256 property
SHA256: str

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.SHA256 '68307A6CBE2804038DF85FB53AEE96AB47EA81439AB2E059DDDEA9F901097D84'

Returns: SHA256 Fingerprint string in hexadecimal and upper case.

authority_key_identifier_digest property
authority_key_identifier_digest: str | None

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.authority_key_identifier_digest '8F85C564F62E39D5A5CA346CA26AAE67029B671E'

Returns: The binary value of the authority key identifier in hexadecimal and upper case or None when certificate has no subject key identifier extension.

private_key_pem_PKCS1 property
private_key_pem_PKCS1: bytes

Examples: >>> cert = CA().issue_cert() >>> cert.private_key_pem_PKCS1 b'-----begin rsa private key----- ...

Raises: ValueError: If the key is not an RSA key. PKCS#1 is RSA-only.

Returns: PEM encoded serialized key in TraditionalOpenSSL format.

private_key_pem_PKCS8 property
private_key_pem_PKCS8: bytes

Examples: >>> cert = CA().issue_cert() >>> cert.private_key_pem_PKCS8 b'-----BEGIN PRIVATE KEY----- ...

Returns: PEM encoded serialized key in PKCS8 format.

public_key property
public_key: CertificateIssuerPublicKeyTypes

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.public_key

Returns: The public key matching this certificate's private key.

public_key_pem property
public_key_pem: bytes

Examples: >>> cert = CA().issue_cert() >>> cert.public_key_pem b'-----BEGIN PUBLIC KEY----- ...

Returns: PEM encoded serialized key in RSAPublicKey format.

subject_key_identifier_digest property
subject_key_identifier_digest: str

Examples: >>> cert = cnert.CA().issue_cert() >>> cert.subject_key_identifier_digest '8F85C564F62E39D5A5CA346CA26AAE67029B671E'

Returns: The binary value of the subject key identifier in hexadecimal and upper case.

Class cnert.CSR

CSR

CSR(
    *sans: str,
    subject_attrs: NameAttrs | None = None,
    private_key: CertificateIssuerPrivateKeyTypes
    | None = None,
    signature_hash: HashAlgorithm
    | str
    | _UnsetType
    | None = _UNSET,
    extensions: Sequence[tuple[ExtensionType, bool]] = (),
    builtin_extensions: bool = True,
)

A CSR object.

Examples: >>> csr = cnert.CSR()

Parameters: sans: Subject Alternative Names as positional arguments subject_attrs: Subject Name Attributes private_key: Private key; generated when omitted. signature_hash: Signature hash algorithm. Defaults to SHA-256, or to None for an Edwards key, which takes no hash. extensions: Extra (extension, critical) pairs. One that collides with a built-in extension replaces it wholesale. builtin_extensions: Add cnert's own extensions. When false, only the supplied pairs are added, subject alternative names included.

Attributes:
  • private_key_pem_PKCS1 (bytes) –

    Raises:

private_key_pem_PKCS1 property
private_key_pem_PKCS1: bytes

Raises: ValueError: If the key is not an RSA key. PKCS#1 is RSA-only.

Returns: PEM encoded serialized key in TraditionalOpenSSL format.

Function build_private_key

build_private_key

build_private_key(
    key_size: int | None = None,
    public_exponent: int | None = None,
    algorithm: str = "rsa",
) -> CertificateIssuerPrivateKeyTypes

Creates a private key.

Examples: >>> build_private_key() # 2048-bit RSA >>> build_private_key(algorithm="ed25519") >>> build_private_key(key_size=1024)

Parameters: key_size: RSA key size, default 2048. RSA only. public_exponent: RSA public exponent, default 65537. RSA only. algorithm: One of cnert.KEY_ALGORITHMS: rsa (default), ed25519, ed448, or an EC curve name such as secp256r1.

Raises: ValueError: If algorithm is unknown, or if key_size or public_exponent is given for a non-RSA algorithm.

Returns: A private key of the requested algorithm.

Key algorithms

cnert.KEY_ALGORITHMS is the tuple of names build_private_key accepts: rsa (the default), ed25519, ed448, and the curve names secp256r1, secp384r1 and secp521r1.

Signature hashes are chosen with the signature_hash argument on Cert, CSR, CA and CA.issue_cert(). It takes a name from cnert.SIGNATURE_HASHES or a cryptography hash object:

import cnert

cert = cnert.CA().issue_cert("example.com", signature_hash="sha512")

It defaults to SHA-256, and to None for an Edwards key, which signs without a separate hash. SHA-1 and MD5 are refused by either form: they cannot sign an X.509 certificate.

Extra extensions

Cert, CSR, CA and CA.issue_cert() take an extensions sequence of (extension, critical) pairs, for anything cnert does not model itself:

from cryptography import x509

import cnert

must_staple = x509.TLSFeature([x509.TLSFeatureType.status_request])
cert = cnert.CA().issue_cert("example.com", extensions=[(must_staple, False)])

A supplied extension whose object identifier matches one cnert adds by itself replaces cnert's version wholesale, keeping its position. That makes the argument an override as well as an addition, and it is the only way to change key usage or basic constraints.

To subtract instead, pass builtin_extensions=False. Only the supplied pairs are then added, subject alternative names included, so a bare certificate carrying exactly one chosen extension is one call:

import cnert

bare = cnert.CA().issue_cert("example.com", builtin_extensions=False)

A certificate built that way is not valid for any real use, and an authority built that way is not a usable authority. That is the point. It is how a parser test proves an absent extension reads as absent.

Supplying keys

CA(), CA.issue_intermediate() and CA.issue_cert() take a private_key, which is how an undersized key or an unusual public exponent becomes reachable:

import cnert

ca = cnert.CA(private_key=cnert.build_private_key(key_size=1024))
cert = ca.issue_cert(
    "example.com", private_key=cnert.build_private_key(public_exponent=3)
)

A supplied key is the certificate's subject key only. The signature always comes from the issuing CA's key. A csr already carries a key, so passing both a csr and a private_key raises ValueError. Passing the same key at several levels is allowed, and is how a key-reuse fixture is built.

Function idna_encode

idna_encode

idna_encode(_string: str) -> str

Creates a valid internationalized domain name

Parameters: _string: Internationalized domain name

Function identity_string_to_x509

identity_string_to_x509

identity_string_to_x509(identity: str) -> GeneralName

Creates a x509.GeneralName from a string.

Parameters: identity: IP Address, DNS name or email address.