REST API¶
Warning
This feature is still experimental, incomplete and will change without any advance notice.
django-ca provides an optional REST API based on Django Ninja. It allows you to list certificate authorities and certificates,
as well as sign and revoke certificates. The API is available at the /api/
sub path. So if you
installed django-ca at https://example.com/django-ca/, the API is available at
https://example.com/django-ca/api/, with Open API documentation available at
https://example.com/django-ca/api/docs/.
The API is disabled by default and depending how you installed django-ca, you might have to install additional dependencies manually.
Installation¶
The REST API requires the api
extra to be installed.
For example, if you use django-ca as Django app, simply install it with:
$ pip install django-ca[api]
The Docker image already has everything installed, so you don’t need to take any extra steps if you use Docker or Docker Compose.
Enable the API¶
To enable the API, you need to set the CA_ENABLE_REST_API setting to True
.
You must also enable API access individually for every certificate authority. This can be done via the admin interface or via the command line. The exact invocation depends on how you installed django-ca:
user@host:~$ python manage.py list_cas # Get CA serial
E4:7C:17:... - Root
user@host:~$ python manage.py edit_ca --api-enable E4:7C:17:...
user@host:~$ django-ca list_cas # Get CA serial
E4:7C:17:... - Root
user@host:~$ django-ca edit_ca --api-enable E4:7C:17:...
user@host:~$ docker exec backend manage list_cas # Get CA serial
E4:7C:17:... - Root
user@host:~$ docker exec backend manage edit_ca --api-enable E4:7C:17:...
user@host:~/ca/$ docker compose exec backend manage list_cas # Get CA serial
E4:7C:17:... - Root
user@host:~/ca/$ docker compose exec backend manage edit_ca --api-enable E4:7C:17:...
Create an API user¶
The API uses the built in Django users and permissions and HTTP Basic Authentication.
The easiest way to add a user is via the Django admin interface. The following permissions are required:
Required permission |
endpoints |
---|---|
Can view Certificate Authority |
|
Can change Certificate Authority |
|
Can sign Certificate |
|
Can view Certificate |
|
Can revoke Certificate |
|
If you do not use the admin interface, Django unfortunately does not provide an out-of-the box command to
create users, so you have to create them via manage.py shell with our small helper function
create_api_user()
:
>>> from django_ca.api.utils import create_api_user
>>> create_api_user('api-username', password='api-password')
Quickstart¶
The API is available under /django_ca/api/
by default, view the documentation under
/django_ca/api/docs
(the /django_ca/
prefix can be removed/modified with the
CA_URL_PATH setting).
In the following subsections you can learn how to use the API to issue a new certificate.
Create a CSR¶
The first step to retrieve a certificate is to create a private key and a certificate signing request (CSR). Note that the contents of the CSR (e.g. the subject) is not used, as the information is provided in the request itself.
user@host:~$ openssl genrsa -out priv.pem 4096
user@host:~$ openssl req -new -key priv.pem -out csr.pem -utf8 -batch -subj '/'
>>> # Generate a private key and simplest possible CSR. See also:
>>> # https://cryptography.io/en/latest/x509/tutorial/
>>> from cryptography import x509
>>> from cryptography.hazmat.primitives import hashes, serialization
>>> from cryptography.hazmat.primitives.asymmetric import rsa
>>> from cryptography.x509.oid import NameOID
>>> key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
>>> csr = x509.CertificateSigningRequestBuilder().subject_name(
... x509.Name([]) # NOTE: The subject in the CSR is not used
... ).sign(key, hashes.SHA256())
Retrieve the serial of the CA¶
If you don’t know the serial of the CA, you can retrieve it using one simple API endpoint:
user@host:~$ curl -u user https://ca.example.com/django_ca/api/ca/
[{"serial": "E47C17...", ...}, ...]
Generate a CSR using cryptography and retrieve a new certificate for example.com
using requests.
>>> import requests
>>> url = "https://ca.example.com/django_ca/api/ca/"
>>> auth = ("user", "password")
>>> serial = requests.get(url, auth=auth).json()[0]["serial"]
Create/poll certificate order¶
Request that the certificate authority issues a new certificate by creating a certificate order. You then poll the order until the certificate is issued:
user@host:~$ curl \
> -u user \
> -H "Content-Type: application/json" \
> -d "{\"csr\": \"`awk '{printf "%s\\\\n", $0}' csr.pem`\", \"subject\": [{\"oid\": \"2.5.4.3\", \"value\": \"example.com\"}]}" \
> https://ca.example.com/django_ca/api/ca/E47C17.../sign/
{"slug": "wj5ryHjWx4OT", "status": "pending", "serial": null, ...}
user@host:~$ curl -u user https://ca.example.com/django_ca/api/ca/E47C17.../orders/wj5ryHjWx4OT/
{"slug": "wj5ryHjWx4OT", "status": "issued", "serial": "3D6CA7...", ...}
>>> csr_pem = csr.public_bytes(serialization.Encoding.PEM).decode('utf-8')
>>> subject = [{"oid": NameOID.COMMON_NAME.dotted_string, "value": "example.com"}]
>>> order = requests.post(
... f"{url}{serial}/sign/",
... auth=auth,
... json={"csr": csr_pem, "subject": subject}
... ).json()
>>> status = order["status"] # equals "pending"
>>>
>>> # Poll the order until finished (BEWARE: in this simplicity, this may loop indefinitely!)
>>> order_url = f"{url}{serial}/orders/{order['slug']}/"
>>> while status == "pending":
... order = requests.get(order_url, auth=auth).json()
... status = order["status"]
Retrieve certificate¶
Once the certificate is issued, it is time to retrieve it:
user@host:~$ curl -u user https://ca.example.com/django_ca/api/ca/E47C17.../certs/3D6CA7.../
{"serial": "3D6CA7...", "pem": "-----BEGIN CERTIFICATE-----\n...", ...}
>>> pem = requests.get(f"{url}{serial}/certs/{order['serial']}/", auth=auth).json()["pem"]
API documentation¶
You can always view the API documentation of your current django-ca version by viewing
https://example.com/django_ca/api/docs. You can also download the current openapi.json
directly.
Below is the documentation for the current version (note that responses are currently not rendered due to this bug):
- GET /django_ca/api/ca/¶
List available certificate authorities
Retrieve a list of currently usable certificate authorities.
- Query Parameters:
expired (boolean) – Include expired CAs.
- Status Codes:
200 OK – OK
- GET /django_ca/api/ca/{serial}/¶
View certificate authority
Retrieve details of the certificate authority with the given serial.
- Parameters:
serial (string) –
- Status Codes:
200 OK – OK
- PUT /django_ca/api/ca/{serial}/¶
Update certificate authority
Update a certificate authority.
All request body fields are optional, so you can also update only individual fields.
- Parameters:
serial (string) –
- Status Codes:
200 OK – OK
- POST /django_ca/api/ca/{serial}/sign/¶
Sign a certificate
Sign a certificate.
The extensions value is optional and allows you to add additional extensions to the certificate. Usually extensions are defined either by the CA or by the named profile.
- Parameters:
serial (string) –
- Status Codes:
200 OK – OK
- GET /django_ca/api/ca/{serial}/orders/{slug}/¶
Retrieve certificate order
Retrieve information about the certificate order identified by slug.
- Parameters:
serial (string) –
slug (string) –
- Status Codes:
200 OK – OK
- GET /django_ca/api/ca/{serial}/certs/¶
List certificates
Retrieve certificates signed by the certificate authority named by serial.
- Parameters:
serial (string) –
- Query Parameters:
autogenerated (boolean) – Include auto-generated certificates (e.g. OCSP responder certificates).
expired (boolean) – Include expired certificates.
profile ({'string', 'null'}) – Only return certificates generated with the given profile.
revoked (boolean) – Include revoked certificates.
- Status Codes:
200 OK – OK
Utility functions¶
- django_ca.api.utils.create_api_user(username: str, password: str, view_certificateauthority: bool = True, change_certificateauthority: bool = True, sign_certificate: bool = True, view_certificate: bool = True, revoke_certificate: bool = True, **extra_fields: Any) User [source]¶
Create an API user capable of using the REST API.
By default, the user will be able to perform all actions provided by the API.
Note that unlike
create_user()
, the password argument is the second argument and mandatory. You can still pass an email address as keyword argument.>>> create_api_user("username", "password", revoke_certificate=False, email="user@example.com") <User: username>
- Parameters:
- usernamestr
The username for the API user.
- passwordstr
The password for the API user.
- view_certificateauthoritybool, optional
If the user is able to list/view certificate authorities via the API.
- change_certificateauthoritybool, optional
If the user is able to update certificate authorities via the API.
- sign_certificatebool, optional
If the user is able to sign new certificates via the API.
- view_certificatebool, optional
If the user is able to list/view existing certificates via the API.
- revoke_certificatebool, optional
If the user is able to revoke certificates via the API.
- **extra_fields
Any additional keyword arguments are passed to
create_user()
.