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.
Metadata
Release files for pki-kit 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pki_kit-0.0.2.tar.gz | 20.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pki_kit-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.0 kB
Release files / pki_kit-0.0.2.tar.gz
| Download URL | pki_kit-0.0.2.tar.gz |
|---|---|
| Size | 20.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b20c167ff0fdaf6d9795a7576c688ebcdba177449797118131a7d1d435abfa4c
|
|
BLAKE2b-256 checksum How to use checksums |
785744f13bb0d494d4b1a1e6eedfda29f9b6b4000b86e4aecf7cf23b1870ddd5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.16
|
Release files / pki_kit-0.0.2-py3-none-any.whl
| Download URL | pki_kit-0.0.2-py3-none-any.whl |
|---|---|
| Size | 33.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5cb32cd43b5e29eec880b45842dc198e3b40e27d79f6157ef70cc047d098126b
|
|
BLAKE2b-256 checksum How to use checksums |
a9613087cdbea352b5cbb4528a1595f3a2a120fcd63f43aa6a6a11fe305053c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.16
|