Skip to main content

Unified Python toolkit for X.509 and Nebula PKI — issue, revoke, and export certificates for mTLS and overlay networks

Project description

pki-kit

A Python library for managing Public Key Infrastructures with a unified API across X.509 and Nebula certificate backends. Issue, revoke, export, and persist certificates programmatically or from the CLI.

Why pki-kit?

Setting up mutual TLS or a Nebula overlay network typically means juggling openssl, nebula-cert, and ad-hoc scripts. pki-kit replaces all of that with a single, typed Python API:

  • Dual backend -- X.509 and Nebula certificates through one interface
  • Pluggable storage -- in-memory (tests/CI) or filesystem with optional SOPS encryption for private keys
  • Idempotent operations -- re-issuing the same request + serial returns the existing certificate
  • Full lifecycle -- issue, revoke, filter, export (PEM, DER, PKCS#12)
  • Type-safe -- Pydantic models, strict mypy, generics throughout
  • CLI included -- pki-kit command for scripting and quick operations

Installation

pip install pki-kit

# with CLI support
pip install pki-kit[cli]

Requires Python 3.12+.

For Nebula backend: install nebula-cert and make sure it's on your PATH.

Quick Start

X.509: Create a CA and issue a leaf certificate

from datetime import UTC, datetime, timedelta

from pki import (
    ExtendedKeyUsage,
    KeyAlgorithm,
    KeyUsage,
    Subject,
    X509CertificateRequest,
    X509Pki,
)

pki = X509Pki()
now = datetime.now(tz=UTC)

# Self-signed CA
ca = pki.issue_certificate(
    X509CertificateRequest(
        subject=Subject(common_name="My Root CA"),
        not_before=now,
        not_after=now + timedelta(days=3650),
        key_algorithm=KeyAlgorithm.EC_P256,
        is_ca=True,
        key_usages=[KeyUsage.CERT_SIGN, KeyUsage.CRL_SIGN],
    )
)

# Leaf certificate signed by the CA
leaf = pki.issue_certificate(
    X509CertificateRequest(
        subject=Subject(common_name="api.example.com"),
        not_before=now,
        not_after=now + timedelta(days=365),
        key_algorithm=KeyAlgorithm.EC_P256,
        san_dns_names=["api.example.com", "*.api.example.com"],
        extended_key_usages=[ExtendedKeyUsage.SERVER_AUTH],
    ),
    ca_serial_number=ca.serial_number,
)

# Export PEM
cert_pem = pki.export_certificate_pem(leaf.serial_number)
key_pem = pki.export_private_key_pem(leaf.serial_number)

# Or grab a PKCS#12 bundle
p12 = pki.export_pkcs12_bytes(leaf.serial_number, password=b"changeit")

Nebula: Create a CA and issue host certificates

from datetime import timedelta

from pki import KeyAlgorithm, NebulaCertificateRequest, NebulaPki, Subject

pki = NebulaPki()

ca = pki.issue_certificate(
    NebulaCertificateRequest(
        subject=Subject(common_name="Nebula CA"),
        duration=timedelta(days=3650),
        is_ca=True,
        key_algorithm=KeyAlgorithm.ED25519,
    )
)

host = pki.issue_certificate(
    NebulaCertificateRequest(
        subject=Subject(common_name="lighthouse-1"),
        duration=timedelta(days=365),
        ip="10.0.0.1/24",
        groups=["lighthouses", "servers"],
        key_algorithm=KeyAlgorithm.ED25519,
    ),
    ca_serial_number=ca.serial_number,
)

Persistent storage with filesystem stores

from pathlib import Path

from pki import X509Pki
from pki.stores import (
    FilesystemCertificateStore,
    FilesystemKeyStore,
    FilesystemStateStore,
    SoftwareCryptoProvider,
)

base = Path("./my-pki")
name = "production"

state_store = FilesystemStateStore(base_dir=base, pki_name=name)
cert_store = FilesystemCertificateStore(base_dir=base, pki_name=name)
key_store = FilesystemKeyStore(base_dir=base, pki_name=name)  # SOPS-encrypted

pki = X509Pki(
    pki_state=state_store.load(),
    certificate_store=cert_store,
    crypto_provider=SoftwareCryptoProvider(key_store=key_store),
)

# ... issue certificates ...

# Persist state
state_store.save(pki.export_state())

CLI

# X.509
pki-kit x509 issue-ca --common-name "My CA" --days 3650 --algorithm ec-p256
pki-kit x509 issue --common-name "server.local" --days 365 --san-dns "server.local"
pki-kit x509 list
pki-kit x509 export-cert <serial>
pki-kit x509 revoke <serial> --reason key-compromise

# Nebula
pki-kit nebula issue-ca --common-name "Nebula CA" --days 3650
pki-kit nebula issue --common-name "host1" --ip "10.0.0.1/24" --groups web,servers
pki-kit nebula list

Set defaults via environment variables:

Variable Description
PKI_KIT_BASE_PATH Root directory for filesystem stores
PKI_KIT_PKI_NAME PKI instance name (default: default)
PKI_KIT_SOPS_ARGS Extra arguments passed to SOPS
PKI_KIT_OUTPUT_FORMAT Output format: TABLE, JSON, YAML

Key Algorithms

Algorithm Backend
RSA_2048 X.509
RSA_4096 X.509
EC_P256 X.509, Nebula
EC_P384 X.509
ED25519 X.509, Nebula

Architecture

Pki (abstract)
├── X509Pki          ─── CryptoProvider ─── KeyStore
└── NebulaPki                                  ├── InMemoryKeyStore
                                               └── FilesystemKeyStore (SOPS)
CertificateStore (generic)
├── InMemoryCertificateStore
└── FilesystemCertificateStore (YAML)

StateStore
├── InMemoryStateStore
└── FilesystemStateStore (YAML)

All stores default to in-memory implementations -- swap in filesystem stores when you need persistence, with no changes to your certificate logic.

Development

uv sync --all-extras
uv run pytest
uv run mypy src
uv run ruff check src tests

License

See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pki_kit-0.0.2.tar.gz (20.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pki_kit-0.0.2-py3-none-any.whl (33.3 kB view details)

Uploaded Python 3

File details

Details for the file pki_kit-0.0.2.tar.gz.

File metadata

  • Download URL: pki_kit-0.0.2.tar.gz
  • Upload date:
  • Size: 20.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.16

File hashes

Hashes for pki_kit-0.0.2.tar.gz
Algorithm Hash digest
SHA256 b20c167ff0fdaf6d9795a7576c688ebcdba177449797118131a7d1d435abfa4c
MD5 9e11b338409d4b0c87aac2bd82c6687a
BLAKE2b-256 785744f13bb0d494d4b1a1e6eedfda29f9b6b4000b86e4aecf7cf23b1870ddd5

See more details on using hashes here.

File details

Details for the file pki_kit-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: pki_kit-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 33.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.16

File hashes

Hashes for pki_kit-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5cb32cd43b5e29eec880b45842dc198e3b40e27d79f6157ef70cc047d098126b
MD5 d50b24c7dcd8a45ef92b2ebb54e3d74a
BLAKE2b-256 a9613087cdbea352b5cbb4528a1595f3a2a120fcd63f43aa6a6a11fe305053c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page