Skip to content
Merged

init #986

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 242 additions & 2 deletions docs/source/features/apis/rest_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ Interactive Documentation

Trustpoint provides interactive API documentation using Swagger UI and ReDoc:

- **Swagger UI**: ``https://<trustpoint-host>/api/schema/swagger-ui/``
- **Swagger UI**: ``https://<trustpoint-host>/swagger/``
- **ReDoc**: ``https://<trustpoint-host>/redoc/``
- **OpenAPI schema**: ``https://<trustpoint-host>/schema/``

The interactive documentation allows you to:

Expand All @@ -96,4 +98,242 @@ Definition

.. openapi:: ../../_static/trustpoint-openapi-swagger.yaml
:group:
:examples:
:examples:

CA and CRL management
--------------------

CA and CRL administration requires authentication and the ``manage_cas``
permission. Private keys and upstream authentication secrets are never included
in these responses. Upload actions accept ``multipart/form-data``; configuration
and enrollment actions accept JSON.

CA inventories
^^^^^^^^^^^^^^

``GET /api/cas/`` and ``GET /api/cas/{id}/`` expose the full CA inventory,
including keyless CAs and autogenerated roots, with parent relationships,
certificate identifiers, and CRL settings. Filter by ``ca_type``, ``is_active``,
or ``parent_ca``. ``/api/issuing-cas/`` retains its issuing-CA-only inventory.

For either inventory:

* ``DELETE /api/cas/{id}/`` or ``DELETE /api/issuing-cas/{id}/`` deletes an
unused CA. Child CAs, unexpired issued certificates, and protected references
prevent deletion.
* ``GET /api/cas/{id}/certificate/?encoding=pem`` downloads its public
certificate. ``encoding=der`` is also supported.
* ``GET /api/cas/{id}/issued-certificates/`` lists its issued certificates,
including public PEM content and validity information.

The certificate download and issued-certificate actions also exist under
``/api/issuing-cas/{id}/``. Downloads return 404 when the CA has no certificate.

Creating issuing CAs
^^^^^^^^^^^^^^^^^^^^

The existing ``POST /api/issuing-cas/`` PEM import contract remains supported:
``private_key_pem``, ``ca_certificate_pem``, optional ``certificate_chain_pem``,
``private_key_password``, and ``unique_name``.

Additional actions:

* ``POST /api/issuing-cas/import-pkcs12/``: upload ``pkcs12_file`` with optional
``pkcs12_password`` and ``unique_name``.
* ``POST /api/issuing-cas/generate/``: supply ``key_type`` to generate the local
AutoGenPKI. The security policy must enable AutoGenPKI, and an active
AutoGenPKI must not already exist.
* ``POST /api/issuing-cas/configure-est/`` and ``configure-cmp/``: configure an
upstream CA using ``unique_name``, ``key_type``, ``remote_host``,
``remote_port``, and ``remote_path``. EST additionally requires
``est_username`` and ``est_password``; CMP requires ``cmp_shared_secret``.
Set ``is_ra_mode`` to true for a registration authority configuration.
* ``POST /api/issuing-cas/configure-external-csr/``: supply ``unique_name`` and
``key_type`` to create a managed key for an externally signed CA.

The backend's supported key types and the active security policy apply to all
creation methods, as they do in the Web UI.

Upstream enrollment and external signing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Associate an existing truststore before upstream enrollment::

POST /api/issuing-cas/12/truststore/
Content-Type: application/json

{"trust_store": 5}

The truststore's intended usage is validated. EST uses a TLS truststore; CMP
uses an Issuing CA Chain truststore. An EST RA first associates an Issuing CA
Chain, then a TLS truststore. The CA-chain step populates the RA certificate
and chain relationships.

``POST /api/issuing-cas/{id}/enroll/`` requests the configured upstream EST or
CMP CA certificate. Certificate content is validated against the selected
profile; the default profile is ``issuing_ca``. For example::

{
"common_name": "Example Issuing CA",
"organization_name": "Example",
"country_name": "DE",
"state_or_province_name": "Berlin",
"days": 365
}

Use ``cert_profile_pk`` to select another profile. Subject/SAN/validity fields
follow that profile's requirements. CMP also accepts an optional integer
``sender_kid``. Upstream client errors return 502.

For external signing, ``POST /api/issuing-cas/{id}/csr/`` accepts the same
certificate-content fields and returns a PEM CSR attachment. Then upload the
issued certificate as the multipart ``certificate`` field to
``POST /api/issuing-cas/{id}/import-certificate/``. The certificate must be a
CA certificate with the required signing usages and must match the managed
private key. Existing certificates cannot be replaced through these actions;
use a rollover instead.

CRL configuration and inventory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``PATCH /api/issuing-cas/{id}/crl-settings/`` accepts any subset of::

{
"crl_cycle_enabled": true,
"crl_cycle_interval_hours": 12,
"crl_validity_hours": 24,
"auto_crl_on_revocation_enabled": true
}

Omitted settings are preserved. Interval, validity, CA activation, and security
policy checks match the Web UI. Scheduling is updated through the existing
CA scheduling service.

``POST /api/issuing-cas/{id}/generate-crl/`` and
``GET /api/issuing-cas/{id}/crl/?encoding=pem`` remain available. Fractional
validity hours are preserved when generating a CRL.

The new inventory tracks historical and unassociated CRLs:

* ``GET /api/crls/``: list CRLs; filter by ``ca``, ``is_active``, or ``crl_number``.
* ``POST /api/crls/``: upload ``crl_file`` in PEM or DER encoding, with optional
``ca`` and ``set_active`` (default true). When a CA is supplied, its subject
and signature must match the CRL. Unassociated CRLs remain inactive.
* ``GET /api/crls/{id}/``: inspect metadata, issuer, PEM contents, and revocations.
Serial numbers are hexadecimal strings to preserve their full precision.
* ``GET /api/crls/{id}/download/?encoding=der``: download PEM or DER contents.
* ``DELETE /api/crls/{id}/``: delete a CRL.

CRL identifiers always refer to CRL records, without falling back to CA IDs.
Importing an active CRL deactivates the previous active CRL in the same
transaction. Duplicate imports return 409 without changing the active CRL.

CA rollovers
^^^^^^^^^^^^

``GET /api/ca-rollovers/`` lists rollover history. Filter by ``old_issuing_ca``,
``new_issuing_ca``, or ``state``; retrieve one with
``GET /api/ca-rollovers/{id}/``.

Plan a rollover with ``POST /api/ca-rollovers/``. Supply ``old_issuing_ca`` and
one of these ``method`` values:

* ``pkcs12``: ``pkcs12_file``, optional ``pkcs12_password`` and ``unique_name``.
* ``separate_files``: ``private_key_file``, ``ca_certificate``, optional
``private_key_file_password``, ``ca_certificate_chain``, and ``unique_name``.
* ``est`` or ``cmp``: the corresponding upstream connection/authentication
fields and ``key_type`` described above, with a new ``unique_name``.

Optional ``transition_scheduled_at`` and ``notes`` apply to every method.
Generated-key replacements remain in ``awaiting_new_ca`` until their CA
certificate is enrolled or uploaded through the issuing CA API.

Advance the rollover through dedicated POST actions:

* ``/api/ca-rollovers/{id}/start/``
* ``/api/ca-rollovers/{id}/transition/``
* ``/api/ca-rollovers/{id}/complete/``
* ``/api/ca-rollovers/{id}/cancel/``

These actions enforce the existing rollover state machine and write the same
audit events as the Web UI. Invalid state transitions return 409. Direct
updates to rollover state fields are not supported.


Certificate details and downloads
--------------------------------

Certificate reads and downloads require authentication, using the existing
certificate API access policy.

``GET /api/certificates/`` now includes serial numbers, SHA-256 fingerprints,
validity dates, status, CA/self-signed flags, issuer certificate identifiers,
and public key algorithm/size. The original ``id`` and ``common_name`` fields
remain available. Existing search, filters, ordering, and pagination apply.

``GET /api/certificates/{id}/`` additionally provides:

* Subject and issuer attributes with OIDs, labels, and RDN grouping. Repeated
distinguished-name attributes are preserved.
* Signature algorithm, signature value, public key information, and PEM contents.
* Every X.509 extension with its OID, criticality, decoded common-extension
values, and exact DER contents as ``der_hex``. Other extension values have
a readable representation alongside their DER contents.
* The known issuer chain as certificate identifiers, from direct issuer to root.
* Revocation date, reason, and CA identifier, or ``null`` when not revoked.
* Associated local issued-credential/device/profile identifiers and metadata,
plus the number of certificates referencing this certificate as their issuer.

Serial numbers and fingerprints are hexadecimal strings, preserving their full
precision. Status codes are ``OK``, ``REV`` (revoked), ``EXP`` (expired), and
``NYV`` (not yet valid); ``status_display`` provides a readable label. X.509
``version`` uses its stored zero-based enumeration (``2`` means version 3).

Single certificate download
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Download a public certificate with::

GET /api/certificates/42/download/?file_format=pem

Supported ``file_format`` values are ``pem`` (default), ``der``, ``pkcs7_pem``,
and ``pkcs7_der``. Responses are binary attachments with the appropriate MIME
type and filename extension. Filenames include certificate IDs so certificates
with identical common names remain distinguishable.

Set ``include_chain=true`` to include known issuer certificates::

GET /api/certificates/42/download/?file_format=pkcs7_pem&include_chain=true

PEM downloads place the selected certificate before its issuers. PKCS#7
containers include the same certificate set. A single DER file cannot contain
multiple certificates: use PEM, PKCS#7, or an archive to download a chain.
Only issuer certificates already present in Trustpoint are included; no
external certificates are fetched.

Multiple certificate download
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Request an archive using JSON::

POST /api/certificates/download/
Content-Type: application/json

{
"certificate_ids": [42, 43],
"file_format": "pem",
"archive_format": "zip",
"include_chain": true
}

``certificate_ids`` must contain between 1 and 1000 positive integers.
``archive_format`` supports ``zip`` (default) and ``tar_gz``. Every selected
certificate is a separate file in the chosen certificate format. With
``include_chain``, known issuer certificates are also separate files, including
when DER is selected. Duplicate selections and shared issuer certificates
appear only once. Archive member names are safe, unique filenames.

Unknown certificate IDs return 404 without producing a partial archive.
Unsupported formats, invalid options, and cyclic issuer relationships return
400. Downloads contain public certificates only, without private keys or
credential bundles.
7 changes: 5 additions & 2 deletions trustpoint/pki/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@
"""
from rest_framework.routers import DefaultRouter

from pki.views.ca_api import CaRolloverViewSet, CaViewSet, CrlViewSet, IssuingCaManagementViewSet
from pki.views.cert_profiles import CertProfileViewSet
from pki.views.certificates import CertificateViewSet
from pki.views.domains import DevIdRegistrationViewSet, DomainViewSet
from pki.views.issuing_cas import IssuingCaViewSet
from pki.views.owner_credentials_api import DevOwnerIdViewSet
from pki.views.truststores import TruststoreViewSet

router = DefaultRouter()
router.register(r'certificates', CertificateViewSet, basename='certificate')
router.register(r'cert-profiles', CertProfileViewSet, basename='cert-profiles')
router.register(r'issuing-cas', IssuingCaViewSet, basename='issuing-ca')
router.register(r'issuing-cas', IssuingCaManagementViewSet, basename='issuing-ca')
router.register(r'cas', CaViewSet, basename='ca')
router.register(r'crls', CrlViewSet, basename='crl-api')
router.register(r'ca-rollovers', CaRolloverViewSet, basename='ca-rollover')
router.register(r'truststores', TruststoreViewSet, basename='truststore')
router.register(r'domains', DomainViewSet, basename='domain')
router.register(r'devid-registrations', DevIdRegistrationViewSet, basename='devid-registration')
Expand Down
4 changes: 2 additions & 2 deletions trustpoint/pki/models/ca.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ def create_new_issuing_ca(

return issuing_ca

def _issue_crl(self, crl_validity_hours: int = 24) -> None:
def _issue_crl(self, crl_validity_hours: float = 24) -> None:
"""Issues a CRL with revoked certificates issued by this CA.

Only issuing CAs can issue CRLs (keyless CAs don't have private keys).
Expand All @@ -750,7 +750,7 @@ def _issue_crl(self, crl_validity_hours: int = 24) -> None:

self.logger.info('CRL generation for CA %s finished.', self.unique_name)

def issue_crl(self, crl_validity_hours: int = 24) -> bool:
def issue_crl(self, crl_validity_hours: float = 24) -> bool:
"""Issues a CRL with revoked certificates issued by this CA.

Args:
Expand Down
Loading
Loading