Adding HTTPS with a Private Certificate and Client Verification for Internal APIs
This article explains how to add a self-signed certificate to a private API and verify that the client is genuine.
Server Certificate
Generate a private certificate
Start by creating a private key:
openssl genrsa -out ssl.key 2048
Next, generate a certificate signing request (CSR):
openssl req -new -key ssl.key -days 3650 -out ssl.csr
The days
parameter controls the certificate validity period.
You will be prompted for a few pieces of information that go into the certificate. Apart from the Common Name, which must match the real domain name or IP, the other fields can be filled with anything.
The resulting ssl.csr
is a CSR. Normally you would send it to a public CA for signing, but that is unnecessary for an internal API.
You can sign the CSR with the private key directly:
openssl x509 -req -in ssl.csr -signkey ssl.key -out ssl.crt
If you plan to sign more CSRs or prefer to issue certificates through a CA certificate, generate your own private CA first:
openssl x509 -req -in ssl.csr -extensions v3_ca -signkey ssl.key -out sign.crt
Then use the CA certificate to sign the CSR:
openssl x509 -req -in ssl.csr -extensions v3_usr -CA sign.crt -CAkey ssl.key -CAcreateserial -days 3650 -out ssl.crt
You now have the server-side private key ssl.key
and certificate ssl.crt
. Deploy them with your server application.
Make the client trust the certificate
Because this certificate is self-signed, clients only trust it if you explicitly configure them to. For a browser, follow the browser-specific flow to trust the certificate.
In my case the client is the Python requests
module. To trust the certificate I specify the verify
parameter when sending the request, pointing it at the CA bundle. You can generate the bundle on the server, but distributing it to every client can be inconvenient. I simply export the certificate (X.509 with chain) from Firefox and reference it in the request:
requests.get('https://example.com', verify='ca.crt')
Validate client requests
HTTPS supports mutual authentication with client certificates, but asking every client to install a certificate is cumbersome, and not every server framework can validate them. Public APIs usually rely on token-based authentication. For sensitive internal APIs you can add a Time-based One Time Password (TOTP) on top of tokens, or adapt the algorithm to your needs. That part is beyond the scope of this article.
References: