Private certificate authorities and mutual TLS protect internal APIs, service-to-service endpoints, and admin interfaces that should reject anonymous or publicly trusted traffic. When the default trust store does not recognize the issuing CA or the server expects a client certificate, cURL needs the same trust anchor and client identity that the protected endpoint requires.
During the TLS handshake, cURL first validates the server certificate chain and hostname, then sends a client certificate only if the server requests one. –cacert points to a PEM CA bundle for one transfer, –capath points to a hashed trust directory, and –cert with –key sends the client certificate and private key for mutual TLS.
Certificate material format and TLS backend both affect the exact command flow. PEM files work directly with –cacert, –cert, and –key, –capath expects a rehashed directory on OpenSSL-style installs, and private keys should stay readable only by the account that runs the request. Temporary bypass options such as –insecure hide real trust problems and should stay out of the final command.
Steps to use client certificates and CA certificates with cURL:
- Reproduce the TLS failure first so the missing trust anchor or client identity is clear.
$ curl --silent --show-error --resolve payments-api.int.example.com:443:192.0.2.24 \ https://payments-api.int.example.com/ --output /dev/null curl: (60) SSL certificate problem: unable to get local issuer certificate
Replace the example hostname, address, port, and certificate paths with the values used by the protected service; –resolve is only needed when a specific IP must be forced during testing.
- Trust the issuing CA with –cacert when the server chain is signed by a private or non-default root.
$ curl --verbose --silent --show-error --cacert ~/pki/corp-root-ca.pem \ --resolve payments-api.int.example.com:443:192.0.2.24 \ https://payments-api.int.example.com/ --output /dev/null \ --write-out 'response=%{response_code}\n' * CAfile: /home/ops/pki/corp-root-ca.pem * SSL certificate verify ok. response=200Use –cacert for one explicit PEM bundle without modifying the host trust store.
On builds that support –ca-native, add it alongside –cacert only when both the native CA store and the custom PEM bundle must be consulted.
- Prepare a hashed CA directory and use –capath when the service distributes several PEM CA files or rotated roots.
$ mkdir -p ~/pki/trust $ cp ~/pki/corp-root-ca.pem ~/pki/trust/ $ c_rehash ~/pki/trust Doing /home/ops/pki/trust $ curl --verbose --silent --show-error --capath ~/pki/trust \ --resolve payments-api.int.example.com:443:192.0.2.24 \ https://payments-api.int.example.com/ --output /dev/null \ --write-out 'response=%{response_code}\n' * CApath: /home/ops/pki/trust response=200For OpenSSL-backed cURL, the directory must be processed with c_rehash before –capath lookups succeed.
- Send the client certificate and matching private key when the server requests mutual TLS authentication.
$ curl --verbose --silent --show-error --cacert ~/pki/corp-root-ca.pem \ --cert ~/pki/payments-api-client.crt \ --key ~/pki/payments-api-client.key \ --resolve payments-api.int.example.com:8443:192.0.2.24 \ https://payments-api.int.example.com:8443/ --output /dev/null \ --write-out 'response=%{response_code}\n' * CAfile: /home/ops/pki/corp-root-ca.pem * (304) (IN), TLS handshake, Request CERT (13): * SSL certificate verify ok. response=200The certificate and key must belong to the same client identity; mismatched files usually fail with curl: (58) loading errors or an aborted handshake.
- Use –cert alone when the PEM file already contains both the client certificate and the private key.
$ cat ~/pki/payments-api-client.crt ~/pki/payments-api-client.key > ~/pki/payments-api-client.pem $ chmod 600 ~/pki/payments-api-client.pem $ curl --silent --show-error --cacert ~/pki/corp-root-ca.pem \ --cert ~/pki/payments-api-client.pem \ --resolve payments-api.int.example.com:8443:192.0.2.24 \ https://payments-api.int.example.com:8443/ --output /dev/null \ --write-out 'response=%{response_code}\n' response=200PKCS#12 archives such as client.p12 or client.pfx use a different cURL flow and should not be treated as PEM files.
- Restrict private key readability before moving the command into shell history, scheduled jobs, or CI runners.
$ chmod 700 ~/pki $ chmod 600 ~/pki/payments-api-client.key ~/pki/payments-api-client.pem $ ls -l ~/pki/payments-api-client.key ~/pki/payments-api-client.pem -rw------- 1 ops ops 1704 Mar 28 22:04 /home/ops/pki/payments-api-client.key -rw------- 1 ops ops 2825 Mar 28 22:04 /home/ops/pki/payments-api-client.pem
Readable private keys allow local users or leaked build logs to impersonate the same mTLS client identity.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
