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-kitcommand 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b20c167ff0fdaf6d9795a7576c688ebcdba177449797118131a7d1d435abfa4c
|
|
| MD5 |
9e11b338409d4b0c87aac2bd82c6687a
|
|
| BLAKE2b-256 |
785744f13bb0d494d4b1a1e6eedfda29f9b6b4000b86e4aecf7cf23b1870ddd5
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cb32cd43b5e29eec880b45842dc198e3b40e27d79f6157ef70cc047d098126b
|
|
| MD5 |
d50b24c7dcd8a45ef92b2ebb54e3d74a
|
|
| BLAKE2b-256 |
a9613087cdbea352b5cbb4528a1595f3a2a120fcd63f43aa6a6a11fe305053c7
|