tiny_ca package

class tiny_ca.BaseDB[source]

Bases: ABC

Abstract contract for the certificate registry database adapter.

Defines all operations that the application layer requires from the persistence tier. Concrete implementations (e.g. SyncDBHandler) provide the actual SQL queries and transaction management.

All methods that return a CertificateRecord must return None (not raise an exception) when the requested record simply does not exist. Exceptions are reserved for genuine infrastructure failures (connection errors, constraint violations, etc.).

abstractmethod delete_by_uuid(uuid)[source]

Permanently delete the certificate record identified by uuid.

This is a hard delete — the row is removed from the database. The caller is responsible for also removing the corresponding filesystem artefacts via BaseStorage.delete_certificate_folder.

Parameters:

uuid (str) – The storage folder UUID that uniquely identifies the certificate.

Returns:

True if a row was found and deleted; False if no matching record existed or if a database error occurred.

Return type:

bool

abstractmethod get_by_name(common_name)[source]

Retrieve the currently active certificate record for a given Common Name.

Implementations must filter to only VALID records so that the caller always receives the live certificate or None — never a revoked or expired one.

Parameters:

common_name (str) – The CN (Common Name) field from the certificate Subject to look up.

Returns:

The active VALID record for common_name, or None if no such record exists.

Return type:

CertificateRecord | None

abstractmethod get_by_serial(serial)[source]

Retrieve a certificate record by its X.509 serial number.

Parameters:

serial (int) – Integer serial number to look up. Implementations are responsible for any type conversion required by the underlying storage format (e.g. converting to str for string-typed database columns).

Returns:

The matching record regardless of its current status (VALID, REVOKED, EXPIRED), or None if no record exists for serial.

Return type:

CertificateRecord | None

abstractmethod get_expiring(within_days=30)[source]

Return VALID certificates that expire within within_days calendar days.

Only records with status == VALID are considered — already-revoked or expired records are excluded.

Parameters:

within_days (int) – Look-ahead window in calendar days. Default: 30.

Returns:

Records ordered by not_valid_after ascending (soonest first). Returns an empty list on error.

Return type:

list[CertificateRecord]

abstractmethod get_revoked_certificates()[source]

Yield certificate records that should appear in the current CRL.

Implementations define their own freshness window (e.g. only records revoked within the past 365 days and not yet expired), but must yield objects that expose at minimum:

  • serial_number — castable to int

  • revocation_date — a datetime object

  • revocation_reason — an integer RFC 5280 reason code

Yields:

CertificateRecord – Records (or row-like objects) for each revoked certificate that falls within the implementation’s CRL inclusion window.

Return type:

Generator[CertificateRecord, None, None]

abstractmethod list_all(status=None, key_type=None, limit=100, offset=0)[source]

Return a paginated list of certificate records with optional filters.

Parameters:
  • status (str | None) – Filter by lifecycle state ("valid", "revoked", "expired"). None returns all statuses.

  • key_type (str | None) – Filter by certificate category ("ca", "service", "device", etc.). None returns all types.

  • limit (int) – Maximum number of records to return. Default: 100.

  • offset (int) – Number of records to skip (for pagination). Default: 0.

Returns:

Matching records ordered by id descending (newest first). Returns an empty list on error.

Return type:

list[CertificateRecord]

abstractmethod register_cert_in_db(cert, uuid, key_type=CertType.DEVICE)[source]

Persist a newly issued certificate to the registry.

Creates a new record with status=VALID populated from the certificate metadata. The implementation must extract the CN from cert.subject and store the full PEM encoding for later retrieval.

Parameters:
  • cert (x509.Certificate) – The issued X.509 certificate object to register.

  • uuid (str) – UUID that identifies the filesystem folder containing the corresponding .pem, .key, and .csr artefact files.

  • key_type (CertType) – Certificate category (CA, USER, SERVICE, DEVICE, INTERNAL). Default: CertType.DEVICE.

Returns:

True if the record was persisted successfully; False if the operation failed (the implementation must log the reason and roll back any partial changes).

Return type:

bool

abstractmethod revoke_certificate(serial_number, reason=<ReasonFlags.unspecified: 'unspecified'>)[source]

Mark a certificate as revoked and record the reason and timestamp.

Implementations must: 1. Look up the record by serial_number filtered to status=VALID. 2. If not found, return (False, <NOT_FOUND status>) without error. 3. Update status, revocation_reason, and revocation_date. 4. Commit atomically; roll back on any exception.

Parameters:
  • serial_number (int) – Serial number of the certificate to revoke.

  • reason (x509.ReasonFlags) – RFC 5280 §5.3.1 revocation reason code. Default: x509.ReasonFlags.unspecified (code 0).

Returns:

(success, status_value) where status_value is implementation- defined (typically a RevokeStatus enum member). True indicates the revocation was committed; False means it was not (reason encoded in status_value).

Return type:

tuple[bool, object]

abstractmethod update_status_expired()[source]

Bulk-update all VALID certificates whose not_valid_after has passed.

Sets status = "expired" for every record where: - status == "valid" - not_valid_after < now (UTC)

This method is intended to be called periodically by a background task (e.g. a cron job or an APScheduler job) so that status queries reflect reality without per-request date comparisons.

Returns:

Number of rows updated. Returns 0 on error (after logging).

Return type:

int

class tiny_ca.BaseStorage[source]

Bases: ABC

Abstract contract for certificate artefact storage backends.

Subclasses persist cryptographic objects (certificates, keys, CSRs, CRLs) to some durable medium and provide a way to remove an entire issuance folder in a single atomic call.

The two operations map directly to the two storage events in the certificate lifecycle:

  • Issuancesave_certificate is called once per artefact (.pem, .key, .csr) with the same uuid_str so all three files end up in the same directory.

  • Revocation / overwritedelete_certificate_folder removes the entire UUID directory in one call, avoiding orphaned files.

abstractmethod delete_certificate_folder(uuid_str, cert_path=None)[source]

Remove the directory that contains all artefacts for a given UUID.

Implementations must be idempotent: if the target directory does not exist, return True (no action was needed) rather than raising an error. Only genuine unexpected failures (permission errors, I/O errors) should return False.

Parameters:
  • uuid_str (str) – UUID string that identifies the sub-directory to delete. This is the value returned as the second element by save_certificate.

  • cert_path (str | Path | None) – Optional sub-path prepended before uuid_str, matching the cert_path used when the artefacts were saved. None means uuid_str is directly under the backend’s root folder.

Returns:

True — the directory was deleted, or did not exist (idempotent). False — an unexpected error occurred during deletion; the

implementation must log the exception.

Return type:

bool

abstractmethod save_certificate(cert, file_name, cert_path=None, uuid_str=None, encoding=None, private_format=None, public_format=None, encryption_algorithm=None, is_add_uuid=True, is_overwrite=False)[source]

Serialise cert and persist it to the storage backend.

The implementation must: 1. Determine the correct serialisation format and file extension from

the type of cert (certificate → .pem, private key → .key, CSR → .csr, public key → .pub, CRL → .pem).

  1. Assemble the output path from cert_path, the UUID directory (when is_add_uuid is True), and file_name + extension.

  2. Auto-generate a UUID when uuid_str is None and is_add_uuid is True; reuse uuid_str when provided (so that multiple calls for the same issuance all land in the same folder).

  3. Honour is_overwrite: raise FileAlreadyExists when False and the target path already exists; silently replace when True.

  4. Return the absolute path to the saved file and the UUID used.

Parameters:
  • cert (CryptoObject) – The cryptographic object to serialise and save.

  • file_name (str) – Base filename without extension (e.g. "ca", "my-service").

  • cert_path (str | Path | None) – Optional sub-directory appended after the backend’s root folder. None saves directly under the root.

  • uuid_str (str | None) – Explicit UUID for the issuance sub-directory. Pass the UUID returned by a previous call to group multiple artefacts in the same folder. None triggers auto-generation. Ignored when is_add_uuid is False.

  • encoding (serialization.Encoding | None) – Serialisation encoding override. None falls back to the implementation’s default (typically PEM).

  • private_format (serialization.PrivateFormat | None) – Format for private-key serialisation (e.g. TraditionalOpenSSL, PKCS8). None uses the implementation default.

  • public_format (serialization.PublicFormat | None) – Format for public-key serialisation (e.g. SubjectPublicKeyInfo). None uses the implementation default.

  • encryption_algorithm (serialization.KeySerializationEncryption | None) – Encryption wrapper for private keys (e.g. NoEncryption, BestAvailableEncryption). None uses the implementation default.

  • is_add_uuid (bool) – When True (default), a UUID subdirectory is inserted into the path. Set to False for singleton files such as CRLs that are regenerated in-place (crl.pem).

  • is_overwrite (bool) – When True, silently replace an existing file at the computed path. When False (default), raise FileAlreadyExists.

Returns:

(absolute_path_to_saved_file, uuid_str_used). The second element is the UUID that was used (auto-generated or the value of uuid_str), or None when is_add_uuid is False.

Return type:

tuple[Path, str | None]

Raises:
  • FileAlreadyExists – If the target file already exists and is_overwrite is False.

  • TypeError – If cert is not a recognised cryptographic type.

class tiny_ca.CAConfig(**data)[source]

Bases: BaseCertificateConfig, BaseCertificateDataModel

Complete configuration for bootstrapping a self-signed root CA certificate.

Combines all Subject identity fields (BaseCertificateDataModel) with the cryptographic validity parameters (BaseCertificateConfig). Passed directly to CertificateFactory.build_self_signed_ca via config.model_dump().

Inherited attributes

common_namestr

CN for the CA. Default: "Internal CA".

organizationstr

O field. Default: "My Company".

countrystr

C field (ISO 3166-1 alpha-2). Default: "UA".

key_sizeint

RSA key size in bits. Default: 2048.

days_validint

Validity in days. Default: 3650.

valid_fromdatetime | None

Explicit validity start; None uses current UTC. Default: None.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Parameters:
class tiny_ca.CAFileLoader(ca_cert_path, ca_key_path, ca_key_password=None, logger=None)[source]

Bases: object

Loads a CA certificate and private key from PEM files on the local filesystem.

Responsibility: file reading and PEM deserialisation only. Does not generate certificates, manage sessions, or perform any cryptographic operations beyond deserialisation.

On construction the loader: 1. Validates that both paths point to existing, regular files with

permitted extensions (see ALLOWED_CERT_EXTENSIONS).

  1. Deserialises the CA certificate and private key from PEM.

  2. Extracts CertificateInfo from the CA certificate’s Subject.

After successful construction all three ICALoader properties are available and will not change for the lifetime of the instance.

Parameters:
  • ca_cert_path (str | Path) – Path to the PEM-encoded CA certificate file.

  • ca_key_path (str | Path) – Path to the PEM-encoded CA private key file.

  • ca_key_password (str | bytes | None) – Optional password protecting the private key. A str value is encoded to bytes using UTF-8 before being passed to the cryptography library. None means the key is unencrypted.

  • logger (Logger | None) – Logger instance for diagnostic messages. Falls back to DEFAULT_LOGGER when None.

__init__(ca_cert_path, ca_key_path, ca_key_password=None, logger=None)[source]
Parameters:
Return type:

None

property base_info: CertificateInfo

Structured metadata extracted from the CA certificate Subject.

Returns:

Contains organization, organizational_unit, country, state, and locality fields; any absent attribute is None.

Return type:

CertificateInfo

property ca_cert: Certificate

The deserialized CA certificate.

Returns:

The CA certificate loaded from ca_cert_path.

Return type:

x509.Certificate

property ca_key: RSAPrivateKey

The deserialized CA private key.

Returns:

The private key loaded from ca_key_path.

Return type:

rsa.RSAPrivateKey

class tiny_ca.CertLifetime[source]

Bases: object

Stateless helper that computes and inspects X.509 certificate validity windows.

All operations are pure functions (no side effects, no shared state) and are therefore safe to call from multiple threads simultaneously.

Use this class to: - Compute a (not_before, not_after) pair for a new certificate. - Extract the not_valid_after / not_valid_before timestamps from an

existing certificate as timezone-aware UTC datetime objects.

static compute(valid_from=None, days_valid=365)[source]

Calculate the (not_before, not_after) validity interval for a new certificate.

If valid_from is None the current UTC time is used as the start of the interval. The end of the interval is valid_from plus days_valid calendar days.

The result is validated to ensure the computed end date has not already passed (which would produce an immediately-invalid certificate).

Parameters:
  • valid_from (datetime | None) – Start of the validity period as a timezone-aware datetime. Pass None to use datetime.now(timezone.utc) automatically.

  • days_valid (int) – Number of calendar days the certificate should remain valid. Default: 365 (one year).

Returns:

(not_before, not_after) both expressed in UTC with tzinfo=timezone.utc.

Return type:

tuple[datetime, datetime]

Raises:

InvalidRangeTimeCertificate – If the computed not_after is earlier than the current UTC time, meaning the certificate would be expired immediately upon issuance.

Examples

>>> start, end = CertLifetime.compute(days_valid=90)
>>> assert (end - start).days == 90
async static compute_async(valid_from=None, days_valid=365)[source]

Async version of compute().

Configures the calculations in the thread pool so as not to block the event loop.

Parameters:
  • valid_from (datetime | None)

  • hour. (The beginning of the window of action (UTC). None → exact UTC)

  • days_valid (int)

  • instructions (Calendar days are trivial. For)

Return type:

tuple[datetime, datetime]

Returns:

  • tuple[datetime, datetime]

  • (not_before, not_after) in UTC.

Raises:

Examples

>>> start, end = await CertLifetime.compute_async(days_valid=90)
>>> assert (end - start).days == 90
static normalize_dt(dt)[source]

Ensure dt is a timezone-aware UTC datetime.

SQLAlchemy’s DateTime column stores naive datetimes (no tzinfo). This helper centralises the normalisation so that lifecycle managers never duplicate the if dt.tzinfo is None guard inline.

Parameters:

dt (datetime) – Any datetime object, aware or naive.

Returns:

The same instant expressed as a UTC-aware datetime. If dt already carries tzinfo, it is returned unchanged. If dt is naive it is assumed to represent UTC and tzinfo is attached via .replace(tzinfo=UTC).

Return type:

datetime

Examples

>>> naive = datetime(2025, 1, 1, 12, 0, 0)
>>> CertLifetime.normalize_dt(naive).tzinfo is UTC
True
static valid_from(cert)[source]

Return the activation timestamp of cert as a timezone-aware UTC datetime.

Wraps cert.not_valid_before_utc and ensures the returned value always carries tzinfo=timezone.utc for safe comparison with other aware datetimes.

Parameters:

cert (x509.Certificate) – The certificate whose activation date should be read.

Returns:

cert.not_valid_before_utc with tzinfo explicitly set to timezone.utc.

Return type:

datetime

async static valid_from_async(cert)[source]

Async version valid_from().

Parameters:
  • cert (x509.Certificate)

  • certificate (The)

  • read. (the date of the beginning of each one needs to be)

Return type:

datetime

Returns:

  • datetime

  • cert.not_valid_before_utc with tzinfo=UTC.

static valid_to(cert)[source]

Return the expiry timestamp of cert as a timezone-aware UTC datetime.

Wraps cert.not_valid_after_utc and ensures the returned value always carries tzinfo=timezone.utc for safe comparison with other aware datetimes.

Parameters:

cert (x509.Certificate) – The certificate whose expiry date should be read.

Returns:

cert.not_valid_after_utc with tzinfo explicitly set to timezone.utc.

Return type:

datetime

async static valid_to_async(cert)[source]

Async version valid_to().

Parameters:
  • cert (x509.Certificate)

  • read. (The certificate and the date of completion must be)

Return type:

datetime

Returns:

  • datetime

  • cert.not_valid_after_utc with tzinfo=UTC.

class tiny_ca.CertType(value)[source]

Bases: Enum

Enumeration of certificate categories issued by the CA.

Each member carries a short string value that is used as a human-readable prefix when encoding serial numbers via _PrefixRegistry and SerialWithEncoding. The prefix makes it possible to identify the certificate category directly from a hex dump of the serial number without any additional tooling.

Members

USER“USR”

End-user personal certificate. Issued to individual people for authentication, email signing, or client TLS.

SERVICE“SVC”

Service or application certificate. Issued to software services, microservices, or API endpoints that need mutual TLS or code signing.

DEVICE“DEV”

Device certificate. Issued to physical or virtual devices (IoT nodes, network equipment) that authenticate to the infrastructure.

INTERNAL“INT”

Internal infrastructure certificate. Issued to internal components such as monitoring agents, message brokers, or CI runners that need identity but are not user-facing.

CA“CA”

Certificate Authority certificate. Used for the root or intermediate CA itself; BasicConstraints(ca=True) is always set for this type.

Notes

The string values are also stored in the key_type column of CertificateRecord so that certificate categories are human-readable in direct SQL queries.

Examples

>>> CertType.SERVICE.value
'SVC'
>>> CertType("DEV")
<CertType.DEVICE: 'DEV'>
CA = 'CA'
DEVICE = 'DEV'
INTERNAL = 'INT'
SERVICE = 'SVC'
USER = 'USR'
class tiny_ca.CertificateFactory(ca_loader, logger=None)[source]

Bases: object

Cryptographic factory for X.509 certificates, CSRs, and CRLs.

CertificateFactory is the single source of all certificate-generation logic in the library. It accepts an ICALoader at construction time and uses the CA certificate and key it provides to sign all issued artefacts.

Responsibilities

  • Generate self-signed root CA certificates (build_self_signed_ca).

  • Issue end-entity certificates signed by the loaded CA (issue_certificate).

  • Build and sign Certificate Revocation Lists (build_crl).

  • Validate an existing certificate against the loaded CA (validate_cert).

Out of scope

  • Writing any files to disk.

  • Recording certificates in a database.

  • Business-level rules (duplicate CN detection, rotation policies, etc.).

type ca_loader:

ICALoader

param ca_loader:

Provider of the CA certificate, private key, and base Subject info. Must satisfy the ICALoader Protocol (see file_loader.py).

type ca_loader:

ICALoader

type logger:

Logger | None

param logger:

Logger for operational messages. Falls back to DEFAULT_LOGGER when None.

type logger:

Logger | None

raises TypeError:

If ca_loader does not implement the ICALoader Protocol.

__init__(ca_loader, logger=None)[source]
Parameters:
Return type:

None

async abuild_crl(revoked_certs, days_valid=1)[source]
Return type:

CertificateRevocationList

Parameters:
build_crl(revoked_certs, days_valid=1)[source]

Build and sign a Certificate Revocation List from the provided records.

Iterates over revoked_certs, adds each entry to the CRL builder, then signs the list with the CA private key. The resulting CRL is valid from the current UTC time until now + days_valid days.

Parameters:
  • revoked_certs (Generator[CertificateRecord, None, None]) – Iterable of revoked certificate records as returned by BaseDB.get_revoked_certificates. Each record must expose serial_number (castable to int) and revocation_date (a datetime object).

  • days_valid (int) – Number of days until the CRL expires and must be regenerated. Typical values are 1 (daily rotation) to 7 (weekly). Default: 1.

Returns:

The signed CRL object. The caller is responsible for persisting it to storage via BaseStorage.

Return type:

x509.CertificateRevocationList

static build_self_signed_ca(common_name='Internal CA', organization='My Company', country='UA', key_size=2048, days_valid=3650, valid_from=None, logger=None)[source]

Generate a self-signed root CA certificate and its private key.

This is a @staticmethod — it requires no loaded CA because the resulting certificate is its own issuer. It is typically called once during bootstrap to establish the trust anchor for the PKI.

The generated certificate includes: - BasicConstraints(ca=True) — marks it as a CA certificate. - KeyUsage with key_cert_sign and crl_sign set to True. - SubjectKeyIdentifier derived from the public key.

Parameters:
  • common_name (str) – Common Name (CN) for the CA Subject / Issuer fields. Default: "Internal CA".

  • organization (str) – Organization (O) field. Default: "My Company".

  • country (str) – Two-letter ISO 3166-1 alpha-2 country code (C field). Default: "UA".

  • key_size (int) – RSA key length in bits. Use 2048 for standard security or 4096 for long-lived roots. Default: 2048.

  • days_valid (int) – Validity period in calendar days. Default: 3650 (≈10 years).

  • valid_from (datetime.datetime | None) – Start of the validity period. None uses the current UTC time.

  • logger (Logger | None) – Optional logger. Falls back to DEFAULT_LOGGER.

Returns:

(certificate, private_key) — both must be persisted by the caller.

Return type:

tuple[x509.Certificate, rsa.RSAPrivateKey]

Raises:

InvalidRangeTimeCertificate – If the computed expiry date is already in the past.

cosign_certificate(cert, days_valid=None, valid_from=None)[source]

Re-sign an existing certificate with this CA’s key and certificate.

Creates a new x509.Certificate that preserves the original Subject, public key, and all v3 extensions, but replaces:

  • Issuer — set to this CA’s Subject.

  • AuthorityKeyIdentifier — updated to reflect this CA’s SKI.

  • Serial number — a fresh serial is generated so the co-signed certificate is distinguishable from the original in CRLs and logs.

  • Validity window — optionally overridden via days_valid and valid_from; when both are None the original window is preserved exactly.

The certificate is signed with SHA-256 using self._ca.ca_key.

Note

This operation does not verify that the original certificate was valid or trusted before co-signing. Call validate_cert() first if pre-validation is required.

Parameters:
  • cert (x509.Certificate) – The source certificate whose Subject, public key, and extensions are copied into the co-signed output.

  • days_valid (int | None) – Override the validity duration in calendar days, counted from valid_from (or now when valid_from is also None). None preserves the original not_valid_before / not_valid_after window unchanged.

  • valid_from (datetime.datetime | None) – Override the start of the validity window. Ignored when days_valid is None. None + days_valid set → uses the current UTC time as the start.

Returns:

A new certificate object identical in content to cert except for the issuer, AKI, serial number, and (optionally) validity window. Must be persisted by the caller.

Return type:

x509.Certificate

Raises:

InvalidRangeTimeCertificate – If days_valid is provided and the computed expiry is already in the past.

Examples

>>> cosigned = factory.cosign_certificate(third_party_cert, days_valid=365)
>>> assert cosigned.issuer == factory._ca.ca_cert.subject
>>> assert cosigned.subject == third_party_cert.subject
export_pkcs12(cert, private_key, password=None, name=None)[source]

Pack cert and private_key into a PKCS#12 (PFX) bundle.

PKCS#12 is the standard container format accepted by Windows certificate stores, macOS Keychain, Java keystores, and most browser import dialogs. The CA certificate is automatically included as the issuer in the chain.

Parameters:
  • cert (x509.Certificate) – The leaf certificate to export.

  • private_key (rsa.RSAPrivateKey) – The private key corresponding to cert’s public key.

  • password (bytes | None) – Optional password to encrypt the PKCS#12 file. None produces an unencrypted bundle (not recommended for production).

  • name (str | None) – Friendly name (alias) embedded in the PKCS#12 bag. Defaults to the certificate’s Common Name when None.

Returns:

Raw DER-encoded PKCS#12 bytes. Write to a .p12 or .pfx file, or send as an HTTP response with Content-Type: application/x-pkcs12.

Return type:

bytes

get_cert_chain(cert)[source]

Return the full certificate chain from cert up to the CA root.

For a single-level PKI (leaf → root CA) this returns [cert, ca_cert]. The list is ordered leaf-first, root-last — the same order expected by nginx ssl_certificate, envoy tls_certificates, and the fullchain.pem convention used by Let’s Encrypt.

Parameters:

cert (x509.Certificate) – The leaf (or intermediate) certificate to start the chain from.

Returns:

[cert, self._ca.ca_cert] — leaf first, CA root last.

Return type:

list[x509.Certificate]

static inspect_certificate(cert)[source]

Extract and return a structured, human-readable summary of cert.

Parses every commonly-used X.509 v3 extension and Subject attribute into plain Python values wrapped in a CertificateDetails dataclass. The method never performs cryptographic verification — use validate_cert() for that. It is therefore safe to call on certificates from any issuer.

Parameters:

cert (x509.Certificate) – The certificate to inspect. May have been issued by this CA or by a completely different PKI.

Returns:

A frozen dataclass with the following fields populated:

  • serial_number — raw integer serial.

  • common_name / organization / country — first matching Subject attribute, or None when absent.

  • issuer_cn — CN from the Issuer field, or None.

  • not_valid_before / not_valid_after — UTC datetimes.

  • is_caTrue when BasicConstraints.ca is True.

  • san_dns / san_ip — lists from the SAN extension.

  • key_usage — list of enabled KeyUsage bit names.

  • extended_key_usage — list of EKU OID dotted strings.

  • fingerprint_sha256 — colon-separated uppercase hex.

  • subject_key_identifier — hex string or None.

  • public_key_size — RSA key bits or None.

Return type:

CertificateDetails

Examples

>>> details = CertificateFactory.inspect_certificate(cert)
>>> print(details.common_name)
'nginx.internal'
>>> print(details.is_ca)
False
>>> print(details.fingerprint_sha256[:8])
'AB:CD:EF'
issue_certificate(common_name, serial_type=CertType.SERVICE, key_size=2048, days_valid=365, valid_from=None, email=None, is_server_cert=False, is_client_cert=False, san_dns=None, san_ip=None)[source]

Issue a signed end-entity certificate for the given subject parameters.

Workflow: 1. Generate a fresh RSA key pair. 2. Build the Subject x509.Name from CA base info + common_name / email. 3. Create a CSR signed with the new private key. 4. Assemble X.509 extensions (KeyUsage, EKU, SAN, SKI, AKI). 5. Sign the certificate with the CA key from self._ca.

The Subject inherits country and organization from the CA’s own certificate so that all issued certificates share a consistent issuer hierarchy.

Parameters:
  • common_name (str) – Common Name (CN) for the new certificate’s Subject.

  • serial_type (CertType) – Certificate category used when encoding the serial number. Default: CertType.SERVICE.

  • key_size (int) – RSA key length in bits. Default: 2048.

  • days_valid (int) – Validity period in calendar days. Default: 365.

  • valid_from (datetime.datetime | None) – Start of the validity period. None uses the current UTC time.

  • email (str | None) – Optional email address added as an emailAddress Subject attribute.

  • is_server_cert (bool) – When True, adds ServerAuth to the Extended Key Usage extension and includes common_name as a DNS SAN (RFC 2818 compliance).

  • is_client_cert (bool) – When True, adds ClientAuth to the Extended Key Usage extension.

  • san_dns (list[str] | None) – Additional DNS names for the Subject Alternative Name extension.

  • san_ip (list[str] | None) – IP addresses (as strings) for the Subject Alternative Name extension.

Returns:

(certificate, private_key, csr) — the certificate and key must be persisted by the caller; the CSR is returned for audit purposes.

Return type:

tuple[x509.Certificate, rsa.RSAPrivateKey, x509.CertificateSigningRequest]

Raises:

InvalidRangeTimeCertificate – If the computed expiry date is already in the past.

issue_intermediate_ca(common_name, key_size=4096, days_valid=1825, valid_from=None, path_length=0, organization=None, country=None)[source]

Issue a subordinate (intermediate) CA certificate signed by this CA.

The resulting certificate has BasicConstraints(ca=True) and KeyUsage(key_cert_sign=True, crl_sign=True) so it can in turn sign leaf certificates. The path_length constraint limits how deep the sub-hierarchy can go.

Parameters:
  • common_name (str) – CN for the intermediate CA Subject.

  • key_size (int) – RSA key size for the intermediate CA key. Defaults to 4096 (recommended for long-lived CA keys).

  • days_valid (int) – Validity in calendar days. Defaults to 1825 (5 years).

  • valid_from (datetime.datetime | None) – Start of the validity window. None uses the current UTC time.

  • path_length (int | None) – BasicConstraints.path_length value. 0 means this intermediate can only sign leaf certificates (cannot create further sub-CAs). None means unlimited sub-levels.

  • organization (str | None) – O field for the intermediate CA Subject. Falls back to the parent CA’s organization when None.

  • country (str | None) – C field. Falls back to the parent CA’s country when None.

Returns:

(intermediate_ca_cert, intermediate_ca_key).

Return type:

tuple[x509.Certificate, rsa.RSAPrivateKey]

Raises:

InvalidRangeTimeCertificate – If the computed expiry is already in the past.

renew_certificate(cert, days_valid=365, valid_from=None)[source]

Issue a renewal of cert with a fresh validity window but the same Subject, public key, and extensions.

Unlike rotate_certificate() (which generates a new key pair), renewal re-uses the existing public key. This is appropriate when the private key has not been compromised and the owner simply needs to extend the validity period.

The renewed certificate receives a new serial number generated by SerialWithEncoding so it is distinguishable from the original in CRLs and audit logs.

Parameters:
  • cert (x509.Certificate) – The certificate to renew. Its Subject, public key, and all v3 extensions (except AKI, which is updated to point to the current CA) are copied verbatim into the renewal.

  • days_valid (int) – Number of days the renewed certificate should be valid. Default: 365.

  • valid_from (datetime.datetime | None) – Start of the new validity window. None uses the current UTC time.

Returns:

A freshly signed certificate with the same identity but a new validity window and serial number.

Return type:

x509.Certificate

Raises:

InvalidRangeTimeCertificate – If the computed expiry is already in the past.

validate_cert(cert)[source]

Verify that cert was issued by this CA, is within its validity window, and carries a cryptographically correct signature.

Three checks are performed in order: 1. Issuer matchcert.issuer must equal the CA’s Subject. 2. Validity window — current UTC time must be between

cert.not_valid_before_utc and cert.not_valid_after_utc.

  1. Signature — the CA public key is used to verify the certificate signature using PKCS#1 v1.5 with the algorithm declared in the cert.

Parameters:

cert (x509.Certificate) – The certificate to validate.

Returns:

Returns silently when all checks pass.

Return type:

None

Raises:

ValidationCertError – If any of the three checks fails. The message describes which check failed and includes the relevant values (timestamps, issuer).

verify_crl(crl)[source]

Verify the signature and validity window of crl.

Checks that: 1. The CRL was signed by this CA’s private key (issuer match + signature). 2. The CRL’s nextUpdate timestamp has not yet passed — i.e. the CRL

is still within its declared validity window.

Parameters:

crl (x509.CertificateRevocationList) – The CRL object to verify.

Returns:

Returns silently when all checks pass.

Return type:

None

Raises:

ValidationCertError – If the CRL issuer does not match this CA, the signature is invalid, or the CRL has expired (nextUpdate is in the past).

Parameters:
  • ca_loader (ICALoader)

  • logger (Logger | None)

class tiny_ca.CertificateRecord(**kwargs)[source]

Bases: Base

ORM model for a single certificate entry in the registry.

Maps to the certificates table. Each row represents one certificate that has been issued by the CA, regardless of its current lifecycle state.

Columns

idint

Auto-incremented surrogate primary key. Not exposed to application code; use serial_number as the business key.

serial_numberstr

X.509 serial number stored as a decimal string. Unique and indexed. String storage avoids integer overflow for 160-bit serials (RFC 5280).

common_namestr

Common Name (CN) extracted from the certificate Subject at issuance time. Not unique; the same CN may appear across different certificate generations (e.g. after rotation).

statusstr

Current lifecycle state. One of the CertificateStatus values: "valid", "revoked", "expired", or "unknown". Defaults to CertificateStatus.VALID on insertion.

not_valid_beforedatetime

Start of the certificate’s validity period (UTC, naive datetime as stored by SQLAlchemy’s DateTime Column type).

not_valid_afterdatetime

End of the certificate’s validity period (UTC, naive datetime). Indexed to allow efficient queries for expired certificates.

key_typestr

Certificate category stored as the CertType enum’s string value (e.g. "ca", "device", "service"). Defaults to CertType.DEVICE.value.

certificate_pemstr

Full PEM-encoded public certificate. Allows reconstruction of the x509.Certificate object without accessing the filesystem.

revocation_datedatetime | None

UTC timestamp at which the certificate was revoked. None for non-revoked certificates.

revocation_reasonint | None

RFC 5280 §5.3.1 revocation reason code stored as an integer. None for non-revoked certificates. Maps to the integer value of the corresponding x509.ReasonFlags member.

uuidstr | None

UUID string that identifies the filesystem folder (managed by BaseStorage) holding the .pem, .key, and .csr files for this certificate. None if no filesystem artefacts exist.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

certificate_pem: Mapped[str]
common_name: Mapped[str]
id: Mapped[int]
key_type: Mapped[str]
not_valid_after: Mapped[datetime]
not_valid_before: Mapped[datetime]
revocation_date: Mapped[datetime]
revocation_reason: Mapped[str]
serial_number: Mapped[str]
status: Mapped[str]
uuid: Mapped[str]
class tiny_ca.CertificateStatus(value)[source]

Bases: StrEnum

Lifecycle state of a certificate record in the registry.

Stored as a lowercase string in the status column of CertificateRecord so the value is human-readable in raw SQL output.

Members

VALID :

The certificate was issued successfully and has not been revoked or expired. Active certificates used for authentication or encryption are expected to be in this state.

REVOKED :

The certificate was explicitly revoked before its natural expiry. The revocation_date and revocation_reason columns on the corresponding CertificateRecord row must be non-null.

EXPIRED :

The certificate’s not_valid_after date has passed. This status may be set by a background job; alternatively, callers can detect expiry by comparing not_valid_after to the current time.

UNKNOWN :

The status could not be determined, typically because no record was found for the requested serial number. Used as a safe sentinel value by CertLifecycleManager.get_certificate_status.

EXPIRED = 'expired'
REVOKED = 'revoked'
UNKNOWN = 'unknown'
VALID = 'valid'
class tiny_ca.ClientConfig(**data)[source]

Bases: CommonNameCertificate, BaseCertificateConfig

Configuration for issuing an end-entity (client or server) certificate.

Passed to CertificateFactory.issue_certificate via config.model_dump(exclude={"name"}).

Parameters:
common_name

CN for the certificate Subject. Inherited from CommonNameCertificate. Default: "Internal CA".

Type:

str

key_size

RSA key size in bits. Inherited from BaseCertificateConfig. Default: 2048.

Type:

int

days_valid

Validity period in calendar days. Default: 3650.

Type:

int

valid_from

Explicit validity start. Default: None.

Type:

datetime | None

serial_type

Certificate category used when encoding the serial number. Default: CertType.SERVICE.

Type:

CertType

is_client_cert

When True, ClientAuth is added to the Extended Key Usage extension. Default: False.

Type:

bool

is_server_cert

When True, ServerAuth is added to the Extended Key Usage extension and the CN is included as a DNS Subject Alternative Name (RFC 2818 compliance). Default: True.

Type:

bool

san_dns

Additional DNS names for the Subject Alternative Name extension. Default: None.

Type:

list[str] | None

san_ip

IP addresses for the Subject Alternative Name extension. Accepts both IPv4 and IPv6. Default: None.

Type:

list[IPvAnyAddress] | None

email

Optional email address added as an emailAddress Subject attribute. Must be a valid RFC 5322 address if provided. Default: None.

Type:

EmailStr | None

name

Override for the output file basename used by BaseStorage. When None, the storage layer derives the name from common_name. This field is excluded from model_dump calls to the factory. Default: None.

Type:

str | None

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

email: EmailStr | None
is_client_cert: bool
is_server_cert: bool
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str | None
san_dns: list[str] | None
san_ip: list[IPvAnyAddress] | None
serial_type: CertType
class tiny_ca.ICALoader(*args, **kwargs)[source]

Bases: Protocol

Protocol that defines the minimum contract for CA-material providers.

Any object that exposes the three properties below satisfies this Protocol and can be injected into CertificateFactory without any inheritance. This makes it trivial to substitute the real filesystem loader with an in-memory stub, an HSM-backed loader, or a mock in unit tests.

Properties

ca_certx509.Certificate

The loaded CA certificate object.

ca_keyrsa.RSAPrivateKey

The loaded CA private key used for signing.

base_infoCertificateInfo

Structured metadata extracted from the CA certificate’s Subject field (organization, country, state, locality, organizational unit).

__init__(*args, **kwargs)
property base_info: CertificateInfo
property ca_cert: Certificate
property ca_key: RSAPrivateKey
class tiny_ca.LocalStorage(base_folder='./certs', base_encoding=Encoding.PEM, base_private_format=PrivateFormat.TraditionalOpenSSL, base_public_format=PublicFormat.SubjectPublicKeyInfo, base_encryption_algorithm=<cryptography.hazmat.primitives._serialization.NoEncryption object>, logger=None)[source]

Bases: BaseStorage

Local filesystem storage backend for certificate artefacts.

Writes serialised cryptographic objects to a configurable directory tree. Each issuance group (certificate + key + CSR) is placed in a dedicated UUID subdirectory so that all artefacts for a given certificate can be found and deleted together.

Directory layout

<base_folder>/
└── [cert_path/]
    └── [<uuid>/]
        ├── <file_name>.pem   # x509.Certificate or CRL
        ├── <file_name>.key   # RSA private key
        ├── <file_name>.csr   # certificate signing request
        └── <file_name>.pub   # RSA public key (if applicable)
type base_folder:

str | Path

param base_folder:

Root directory under which all certificates are stored. The directory is created on first write if it does not exist. Default: "./certs".

type base_folder:

str | Path

type base_encoding:

Encoding

param base_encoding:

Default encoding for all serialised objects. Default: Encoding.PEM.

type base_encoding:

serialization.Encoding

type base_private_format:

PrivateFormat

param base_private_format:

Default format for RSA private-key files. Default: PrivateFormat.TraditionalOpenSSL (PKCS#1, OpenSSL-compatible).

type base_private_format:

serialization.PrivateFormat

type base_public_format:

PublicFormat

param base_public_format:

Default format for RSA public-key files. Default: PublicFormat.SubjectPublicKeyInfo (PKCS#8 / X.509 SubjectPublicKeyInfo).

type base_public_format:

serialization.PublicFormat

type base_encryption_algorithm:

KeySerializationEncryption

param base_encryption_algorithm:

Default encryption applied to private-key files. Default: NoEncryption() — keys are stored in plaintext.

type base_encryption_algorithm:

serialization.KeySerializationEncryption

type logger:

Logger | None

param logger:

Logger for diagnostic messages. Falls back to DEFAULT_LOGGER.

type logger:

Logger | None

__init__(base_folder='./certs', base_encoding=Encoding.PEM, base_private_format=PrivateFormat.TraditionalOpenSSL, base_public_format=PublicFormat.SubjectPublicKeyInfo, base_encryption_algorithm=<cryptography.hazmat.primitives._serialization.NoEncryption object>, logger=None)[source]
Parameters:
Return type:

None

delete_certificate_folder(uuid_str, cert_path=None)[source]

Recursively remove the directory identified by uuid_str.

The target path is resolved as:

<base_folder> / [cert_path/] / <uuid_str>

The operation is idempotent: if the directory does not exist a UserWarning is emitted and True is returned (no action needed). If the path exists but is a regular file rather than a directory, a UserWarning is emitted and True is returned (not our directory). Only a genuine OSError during shutil.rmtree causes False.

Parameters:
  • uuid_str (str) – UUID sub-directory name to remove.

  • cert_path (str | Path | None) – Optional sub-path under base_folder containing uuid_str.

Returns:

True — directory removed, or path was already absent. FalseOSError occurred; check logs for details.

Return type:

bool

Warns:

UserWarning – If the target path does not exist or is not a directory.

save_certificate(cert, file_name, cert_path=None, uuid_str=None, encoding=None, private_format=None, public_format=None, encryption_algorithm=None, is_add_uuid=True, is_overwrite=False)[source]

Serialise cert and write the result to the local filesystem.

Assembles the output path as:

<base_folder> / [cert_path/] / [<uuid>/] / <file_name><ext>

Where ext is determined automatically from the type of cert.

Parameters:
  • cert (CryptoObject) – Cryptographic object to serialise and persist.

  • file_name (str) – Base filename without extension (e.g. "ca", "nginx").

  • cert_path (str | Path | None) – Optional sub-directory appended after base_folder.

  • uuid_str (str | None) – Reuse an existing UUID directory by passing the value returned by a previous save_certificate call. None auto-generates a new UUID. Ignored when is_add_uuid is False.

  • encoding (serialization.Encoding | None) – Encoding override. None uses base_encoding.

  • private_format (serialization.PrivateFormat | None) – Private-key format override. None uses base_private_format.

  • public_format (serialization.PublicFormat | None) – Public-key format override. None uses base_public_format.

  • encryption_algorithm (serialization.KeySerializationEncryption | None) – Private-key encryption override. None uses base_encryption_algorithm.

  • is_add_uuid (bool) – When True (default), a UUID subdirectory is inserted. Set to False for singleton files such as CRL that are regenerated in-place.

  • is_overwrite (bool) – When True, silently replace an existing file. When False (default), raise FileAlreadyExists.

Returns:

(absolute_path_to_written_file, uuid_used). uuid_used is None when is_add_uuid is False.

Return type:

tuple[Path, str | None]

Raises:
  • FileAlreadyExists – If the computed target path already exists and is_overwrite is False.

  • TypeError – If cert is not a supported cryptographic type.

Parameters:
  • base_folder (str | Path)

  • base_encoding (serialization.Encoding)

  • base_private_format (serialization.PrivateFormat)

  • base_public_format (serialization.PublicFormat)

  • base_encryption_algorithm (serialization.KeySerializationEncryption)

  • logger (Logger | None)

class tiny_ca.RevokeStatus(value)[source]

Bases: Enum

Enumerated outcomes for a certificate revocation attempt.

Used as the second element of the (bool, RevokeStatus) tuple returned by BaseDB.revoke_certificate implementations. The boolean indicates overall success; this enum provides the machine-readable reason when the operation did not succeed, and a confirmation token when it did.

Members

NOT_FOUND :

No active (VALID) certificate with the requested serial number exists in the registry. The certificate may already be revoked, expired, or was never registered.

UNKNOWN_ERROR :

An unexpected internal error (e.g. database constraint violation, connection failure) prevented the revocation. The implementation must log the underlying exception before returning this status.

OK :

The revocation was committed successfully. The certificate record has been updated with status=REVOKED, a revocation_date, and the provided revocation_reason.

Examples

>>> success, status = db.revoke_certificate(serial=12345, reason=ReasonFlags.key_compromise)
>>> if not success:
...     if status == RevokeStatus.NOT_FOUND:
...         logger.warning("Certificate not found")
...     else:
...         logger.error("Internal revocation error")
NOT_FOUND = 'The certificate was not revoked because there is no valid certificate with the specified serial number.'
OK = 'success'
UNKNOWN_ERROR = 'The certificate was not revoked due to an internal error. Please review the service logs.'
class tiny_ca.SerialWithEncoding[source]

Bases: object

Stateless serial-number generator that encodes a short name prefix and a UUID-derived random fragment into a single integer.

Serial number layout

[ 16-bit prefix ][ 80-bit encoded name ][ 64-bit random ]

Total width: 160 bits (well within Python’s arbitrary-precision int; X.509 allows up to 20 bytes / 160 bits per RFC 5280 §4.1.2.2).

  • prefix — 2-byte ASCII code from _PrefixRegistry.

  • encoded name — up to 4 ASCII characters packed into 32 bits

    (little-endian byte order, zero-padded).

  • random — lower 64 bits of a fresh uuid.uuid4() ensuring

    global uniqueness without shared state.

Because no mutable state is kept, this class is safe to use from multiple threads or processes simultaneously.

Class Attributes

RANDOM_BITSint

Number of bits reserved for the random (UUID) portion. Default: 64.

NAME_BITSint

Number of bits reserved for the encoded name portion. Default: 32 (4 bytes × 8 bits).

MAX_NAME_LENGTHint

Maximum number of ASCII characters that can be encoded. Default: 4.

MAX_NAME_LENGTH: int = 10

Maximum number of characters accepted by _encode_name().

NAME_BITS: int = 80

Bit-width of the encoded-name segment (10 ASCII chars × 8 bits).

RANDOM_BITS: int = 64

Bit-width of the random (UUID) segment.

classmethod generate(name, serial_type)[source]

Generate a globally unique serial number for name and serial_type.

Only the first MAX_NAME_LENGTH characters of name are encoded; uniqueness is guaranteed by the UUID random segment, not by the name.

Parameters:
  • name (str) – Human-readable identifier. Only the first 4 ASCII characters are embedded; the remainder is ignored (not hashed or truncated with loss).

  • serial_type (CertType) – Certificate category; determines the 2-byte prefix.

Returns:

Non-negative integer serial suitable for X.509 certificates.

Return type:

int

Raises:

KeyError – If serial_type has no registered prefix.

Examples

>>> serial = SerialWithEncoding.generate("nginx", CertType.SERVICE)
>>> cert_type, name = SerialWithEncoding.parse(serial)
>>> assert cert_type == CertType.SERVICE
>>> assert name == "ngin"  # only first 4 chars are stored
classmethod parse(serial)[source]

Decode a serial number produced by generate().

Parameters:

serial (int) – Integer serial number to decode.

Returns:

(cert_type, name_prefix) where name_prefix is the up-to-4-char string recovered from the encoded-name segment. cert_type is None if the prefix is unrecognised.

Return type:

tuple[CertType | None, str]

Examples

>>> serial = SerialWithEncoding.generate("ca-root", CertType.CA)
>>> cert_type, name = SerialWithEncoding.parse(serial)
>>> assert cert_type == CertType.CA
>>> assert name == "ca-r"
class tiny_ca.SyncDBHandler(db_url, logger=None)[source]

Bases: BaseDB

Synchronous, SQLAlchemy-backed certificate registry.

Implements the full BaseDB contract with explicit, atomic transaction management. Every public method follows the same pattern:

  1. Open a new session via self._db.session().

  2. Execute the query / mutation inside a try block.

  3. Commit on success or roll back on any exception.

  4. Always close the session in the finally block.

This guarantees that no session is leaked regardless of outcome, and that partial writes are never visible to other readers.

Parameters:
  • db_url (str) – SQLAlchemy database URL forwarded to DatabaseManager.

  • logger (Logger | None) – Logger for operational and diagnostic messages. Falls back to DEFAULT_LOGGER when None.

__init__(db_url, logger=None)[source]
Parameters:
Return type:

None

delete_by_uuid(uuid)[source]

Permanently delete the certificate record identified by uuid.

Parameters:

uuid (str) – Storage folder UUID of the certificate to delete.

Returns:

True if a row was deleted; False otherwise.

Return type:

bool

get_by_name(common_name)[source]

Fetch the active VALID certificate record for the given Common Name.

Only records with status == CertificateStatus.VALID are returned. Revoked and expired records are ignored so that the caller always receives the currently-active certificate for a given CN, or None if no active certificate exists.

Parameters:

common_name (str) – The CN (Common Name) value from the certificate Subject field.

Returns:

The matching VALID record, or None if absent or on DB error.

Return type:

CertificateRecord | None

get_by_serial(serial)[source]

Fetch a single certificate record by its X.509 serial number.

The serial is stored as a string in the database (to avoid integer overflow across all backends); the conversion is handled internally.

Parameters:

serial (int) – Integer serial number to look up.

Returns:

The matching ORM record, or None if no record exists for serial or if a database error occurs.

Return type:

CertificateRecord | None

get_expiring(within_days=30)[source]

Return VALID certificates expiring within within_days calendar days.

Parameters:

within_days (int) – Look-ahead window in days. Default: 30.

Returns:

Records ordered by not_valid_after ascending. Empty list on error.

Return type:

list[CertificateRecord]

get_revoked_certificates()[source]

Yield revoked certificate rows relevant for the current CRL window.

A record is included when all of the following conditions hold:

  1. revocation_date is not NULL — the certificate was actually revoked.

  2. not_valid_after > now — the certificate has not yet expired; expired certificates need not appear in a CRL because relying parties will reject them regardless.

  3. revocation_date > now - 365 days — the revocation is recent enough to be relevant. This prevents unbounded CRL growth from very old entries that no relying party could still encounter.

Only three columns are selected (serial_number, revocation_date, revocation_reason) to minimise data transfer; callers must not access other CertificateRecord attributes on the yielded rows.

Yields:

CertificateRecord – SQLAlchemy Row objects with serial_number, revocation_date, and revocation_reason attributes.

Return type:

Generator[CertificateRecord, None, None]

Notes

All rows are fetched in a single query before yielding begins. The session is closed in the finally block; do not use the yielded rows after the generator has been exhausted or abandoned.

Return type:

Generator[CertificateRecord, None, None]

list_all(status=None, key_type=None, limit=100, offset=0)[source]

Return a paginated list of certificate records with optional filters.

Parameters:
  • status (str | None) – Filter by lifecycle state. None returns all statuses.

  • key_type (str | None) – Filter by certificate category. None returns all types.

  • limit (int) – Maximum number of records to return. Default: 100.

  • offset (int) – Number of records to skip (pagination). Default: 0.

Returns:

Records ordered by id descending. Empty list on error.

Return type:

list[CertificateRecord]

register_cert_in_db(cert, uuid, key_type=CertType.DEVICE)[source]

Persist a newly issued certificate to the registry.

Creates a new CertificateRecord row with status=VALID from the metadata and PEM encoding of cert. The full PEM is stored so the certificate can be reconstructed independently of the filesystem.

Parameters:
  • cert (x509.Certificate) – The issued X.509 certificate. Its Subject must contain at least one commonName (CN) attribute.

  • uuid (str) – UUID string that identifies the storage folder holding the corresponding .pem, .key, and .csr files.

  • key_type (CertType) – Certificate category. Stored as its str value (e.g. "device"). Default: CertType.DEVICE.

Returns:

True if the record was committed successfully; False if the operation was rolled back due to an error (e.g. duplicate serial, constraint violation).

Return type:

bool

Raises:

IndexError – Re-raised if the certificate contains no CN attribute, indicating a malformed certificate that should not be stored.

revoke_certificate(serial_number, reason=<ReasonFlags.unspecified: 'unspecified'>)[source]

Mark a certificate as revoked and record the reason and timestamp.

Looks up the certificate by serial_number filtered to status == VALID — already-revoked or unknown serials are treated as not found. On success the record is updated in-place: - statusCertificateStatus.REVOKED - revocation_reason → integer value of reason - revocation_date → current UTC timestamp

The change is committed atomically; a rollback is performed on any unexpected error.

Parameters:
  • serial_number (int) – Serial number of the certificate to revoke.

  • reason (x509.ReasonFlags) – RFC 5280 §5.3.1 revocation reason code. Default: x509.ReasonFlags.unspecified (code 0).

Returns:

(True,  RevokeStatus.OK) — revocation committed. (False, RevokeStatus.NOT_FOUND) — no VALID cert with that serial. (False, RevokeStatus.UNKNOWN_ERROR) — unexpected internal error.

Return type:

tuple[bool, RevokeStatus]

update_status_expired()[source]

Bulk-set status=expired for all VALID certs whose validity has passed.

Returns:

Number of rows updated. 0 on error.

Return type:

int

tiny_ca.SyncDatabaseManager

alias of DatabaseManager

Subpackages

Submodules