Skip to content

Certificates

Certificates are issued by a Certificate Authority and can be of type server, client, or ca.

Issue a Certificate

Certificates are always created under a specific CA, identified by the ca_id query parameter.

curl -s -X POST "http://localhost:8000/api/v1/certificates/?ca_id=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "api.example.com",
    "subject_dn": "CN=api.example.com,O=Acme Corp,C=US",
    "certificate_type": "server",
    "key_size": 2048,
    "valid_days": 365,
    "include_private_key": true
  }' | python -m json.tool
Field Required Default Description
common_name Yes Common Name for the certificate
subject_dn Yes Full distinguished name
certificate_type Yes server, client, or ca
key_size No CERT_KEY_SIZE (2048) RSA key size in bits
valid_days No CERT_DAYS (365) Validity period in days
include_private_key No true Whether to generate and store a private key
san_dns_names No DNS Subject Alternative Names (server/dual-purpose)
san_ip_addresses No IP Subject Alternative Names (server/dual-purpose)
san_email_addresses No Email Subject Alternative Names (client/dual-purpose)

Required permission: create_cert capability on the target CA, Admin role in the same org, or Superuser.

Subject Alternative Names

Modern TLS clients ignore the Common Name and rely on SANs. FastPKI auto-populates sensible defaults:

  • Server / dual-purpose certificates get a DNS SAN equal to the Common Name when no san_dns_names are provided.
  • Client certificates whose Common Name parses as an email get an email SAN when no san_email_addresses are provided.

SAN types are constrained by certificate type: servers reject email SANs; clients reject DNS and IP SANs. Violations return 400.

curl -s -X POST "http://localhost:8000/api/v1/certificates/?ca_id=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "api.example.com",
    "subject_dn": "CN=api.example.com,O=Acme Corp,C=US",
    "certificate_type": "server",
    "san_dns_names": ["api.example.com", "api-v2.example.com"],
    "san_ip_addresses": ["10.0.0.42"]
  }' | python -m json.tool

Client Certificate Example

curl -s -X POST "http://localhost:8000/api/v1/certificates/?ca_id=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "alice",
    "subject_dn": "CN=alice,O=Acme Corp,C=US",
    "certificate_type": "client"
  }' | python -m json.tool

Sign a Certificate Signing Request

When the private key must stay on the requesting host — for example, a cert-manager controller, a load balancer provisioning its own keys, or a compliance workflow that forbids the CA from ever seeing the key — submit a CSR instead of calling the create endpoint.

# Generate a key and CSR locally
openssl req -new -newkey rsa:2048 -nodes \
  -keyout api.example.com.key \
  -out api.example.com.csr \
  -subj "/CN=api.example.com/O=Acme Corp/C=US"

# Submit the CSR for signing
curl -s -X POST http://localhost:8000/api/v1/certificates/sign-csr \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -Rs --arg type server --arg ca "Acme Issuing CA" \
        '{csr: ., certificate_type: $type, ca_name: $ca}' \
        < api.example.com.csr)" | python -m json.tool

The CA is selected by ca_id or ca_name (one required). ca_name is scoped to the caller's organization; superusers may resolve any CA by name. Subject, SANs, and the public key are extracted from the CSR; pass explicit common_name, subject_dn, or san_* fields to override.

The response contains only the signed certificate — never a private key, since the requester already holds it.

Required permission: create_cert capability on the resolved CA, Admin in the same org, or Superuser. Audit-logged.

List Certificates

# All certificates visible to the current user
curl -s http://localhost:8000/api/v1/certificates/ \
  -H "Authorization: Bearer $TOKEN" | python -m json.tool

# Filter by issuing CA
curl -s "http://localhost:8000/api/v1/certificates/?ca_id=1" \
  -H "Authorization: Bearer $TOKEN" | python -m json.tool

Superusers see all certificates. Other users see only certificates belonging to their organization.

View a Certificate

curl -s http://localhost:8000/api/v1/certificates/1 \
  -H "Authorization: Bearer $TOKEN" | python -m json.tool

View a Certificate with Private Key

curl -s http://localhost:8000/api/v1/certificates/1/private-key \
  -H "Authorization: Bearer $TOKEN" | python -m json.tool

Required permission: export_private_key capability, Admin in the same org, or Superuser. Audit-logged.

Revoke a Certificate

curl -s -X POST http://localhost:8000/api/v1/certificates/1/revoke \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Key compromise"
  }' | python -m json.tool
Field Required Description
reason No Human-readable revocation reason

The certificate status changes to revoked and a CRL entry is created. Revoking an already-revoked certificate returns 409 Conflict.

Required permission: revoke_cert capability, Admin in the same org, or Superuser. Audit-logged.