Resolving ‘OpenSSL self signed certificate in certificate chain’ Errors on Debian 12 Bookworm
Fix OpenSSL validation errors on Debian 12 Bookworm when connecting to services using self-signed or private CA certificates. Learn to manage trusted CAs.
Fix OpenSSL validation errors on Debian 12 Bookworm when connecting to services using self-signed or private CA certificates. Learn to manage trusted CAs.
This guide addresses a common SSL/TLS certificate validation error encountered on Debian 12 Bookworm systems: "OpenSSL self signed certificate in certificate chain." This issue typically arises when your system or an application running on it attempts to connect to a server presenting a certificate that is either self-signed, or issued by a custom/private Certificate Authority (CA) whose root certificate is not inherently trusted by the client's operating system. Resolving this involves explicitly teaching your Debian system to trust the issuer of the certificate.
Symptom & Error Signature
Users will typically encounter this issue when a client application (e.g., curl, wget, a Docker client, a PHP script, or apt for a custom repository) attempts to establish a secure connection to a server using a certificate not verifiable against the system's trusted CA store. The exact error message can vary depending on the application, but the core issue points to a failure in certificate chain validation.
Common Error Outputs:
1. curl:
$ curl https://your-untrusted-server.com/api/data
curl: (60) SSL certificate problem: self-signed certificate in certificate chain
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
2. wget:
$ wget https://your-untrusted-server.com/file.txt
--2026-09-09 10:30:00-- https://your-untrusted-server.com/file.txt
Resolving your-untrusted-server.com (your-untrusted-server.com)... 192.0.2.10
Connecting to your-untrusted-server.com (your-untrusted-server.com)|192.0.2.10|:443... connected.
ERROR: cannot verify your-untrusted-server.com's certificate, issued by 'CN=Your Custom CA':
Self-signed certificate in certificate chain.
To connect to your-untrusted-server.com insecurely, use '--no-check-certificate'.
3. PHP (e.g., Guzzle HTTP client):
// In PHP error logs or script output
PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: error:0A000086:SSL routines::certificate verify failed in /path/to/your/script.php on line X
PHP Warning: stream_socket_client(): Failed to enable crypto in /path/to/your/script.php on line X
PHP Warning: stream_socket_client(): unable to connect to ssl://your-untrusted-server.com:443 (Unknown error) in /path/to/your/script.php on line X
4. Docker (pulling from a private registry):
$ docker pull your-private-registry.com/myimage:latest
Error response from daemon: Get "https://your-private-registry.com/v2/": x509: certificate signed by unknown authority
Root Cause Analysis
The "self-signed certificate in certificate chain" error indicates a Public Key Infrastructure (PKI) trust issue. When a client initiates an SSL/TLS handshake, the server presents its digital certificate. The client then attempts to validate this certificate by tracing its chain of trust back to a trusted Root Certificate Authority (CA).
The underlying reasons for this specific error are typically one or more of the following:
- Untrusted Root CA: The server's certificate is issued by an Intermediate CA, which in turn is signed by a Root CA. This Root CA's certificate is not present in the client system's default bundle of trusted certificates (e.g.,
/etc/ssl/certs/ca-certificates.crt). This is common in enterprise environments using their own internal CAs or development environments with self-signed certificates. - Self-Signed Intermediate Certificate: One of the certificates within the chain (an Intermediate CA certificate) is itself self-signed, and this specific self-signed certificate is not trusted by the client system.
- Missing Intermediate Certificates on Server: Although less common for the "self-signed in chain" error, sometimes the server fails to provide the full certificate chain during the TLS handshake. If the client doesn't have the intermediate CA certificate(s) in its local trust store, it cannot complete the chain validation back to a trusted root.
- Application-Specific Trust Stores: Some applications or programming languages (e.g., Java, Python, PHP) might maintain their own independent trust stores, or have configurations that override the system-wide trust store. If the custom CA is added only to the system trust store but not to an application's specific configuration, the error will persist for that application.
- Expired or Invalid Certificates (Less Likely for this Specific Error): While general certificate problems can lead to validation failures, the "self-signed in certificate chain" message specifically points to a trust path issue rather than an expiry or malformation issue of the leaf certificate itself.
Step-by-Step Resolution
The primary solution involves adding the custom Root CA certificate (or the self-signed intermediate CA certificate) to your Debian 12 system's trusted certificate store.
1. Obtain the CA Certificate(s)
You need the public certificate file(s) for the Root CA (and potentially any Intermediate CAs) that issued the server's certificate. These files are typically in .crt or .pem format.
From your CA Administrator: This is the most reliable method. They should provide you with the necessary
.crtor.pemfiles.Extracting from the server (if accessible): You can use
openssl s_clientto connect to the server and extract the certificate chain.# Replace your-untrusted-server.com with the actual hostname # The -showcerts option displays the entire certificate chain # The output from '-----BEGIN CERTIFICATE-----' to '-----END CERTIFICATE-----' # needs to be copied for each certificate in the chain, starting from the Root CA. openssl s_client -showcerts -connect your-untrusted-server.com:443 </dev/nullWhen extracting certificates from the server, you will see multiple
-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----blocks. The last certificate in the output is usually the Root CA (or the self-signed intermediate), which is what you need to trust. Copy only that block (or the relevant Root/Intermediate CA certificates) into a new file, saycustom_ca.crt.
2. Place the CA Certificate(s) in the System Trust Store
Once you have the CA certificate file(s) (e.g., custom_ca.crt), you need to place them in the appropriate directory for Debian to recognize them.
# Create a directory for custom CA certificates if it doesn't exist
sudo mkdir -p /usr/local/share/ca-certificates/extra
# Copy your CA certificate file into this directory
# Replace custom_ca.crt with the actual filename of your CA certificate
sudo cp custom_ca.crt /usr/local/share/ca-certificates/extra/custom_ca.crt
# Ensure correct permissions (readable by all)
sudo chmod 644 /usr/local/share/ca-certificates/extra/custom_ca.crt
3. Update the System's CA Certificate Bundle
After placing the new CA certificate, you must instruct Debian to regenerate its system-wide trusted CA bundle.
sudo update-ca-certificates
You should see output similar to this, indicating your custom CA was added:
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
The
update-ca-certificatescommand creates/updates/etc/ssl/certs/ca-certificates.crt, which is a concatenated file containing all trusted certificates, and creates symlinks in/etc/ssl/certs/to individual.crtfiles. Most applications configured to use the system trust store will rely on these.
4. Verify the Trust
Now, re-run the curl or wget command that previously failed. It should now succeed.
$ curl https://your-untrusted-server.com/api/data
# Expected output: the data from your server, no SSL error.
You can also use openssl verify to explicitly check your certificate against the system's trust store:
# If you have the server's leaf certificate (server.crt)
# This command verifies server.crt using the system's default CA path
openssl verify server.crt
# If it fails, specify the CA bundle directly
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt server.crt
# If it succeeds, the output will be "server.crt: OK"
5. Application-Specific Considerations
While update-ca-certificates typically handles most system-level applications, some specific services might require additional steps.
5.1. Docker Daemon
If you are pulling images from a private Docker registry that uses your custom CA:
# Create the directory for the registry's certificates
# Replace your-private-registry.com with your registry's hostname and port
sudo mkdir -p /etc/docker/certs.d/your-private-registry.com:443
# Copy your CA certificate into this directory, naming it ca.crt
sudo cp custom_ca.crt /etc/docker/certs.d/your-private-registry.com:443/ca.crt
# Restart the Docker daemon for changes to take effect
sudo systemctl restart docker
5.2. PHP Applications
Many PHP applications rely on curl or stream_context_create functions. If curl is used, the system-wide update should resolve it. For stream_context_create, PHP typically looks for openssl.cafile or openssl.capath in php.ini.
# Edit your php.ini file (e.g., for php-fpm or CLI)
# Find the relevant php.ini path:
# php --ini (for CLI)
# php-fpm -i | grep 'Loaded Configuration File' (for FPM)
sudo nano /etc/php/8.2/cli/php.ini # Example path
# Ensure these lines are commented out or set correctly:
;openssl.cafile=
;openssl.capath=
# If you must explicitly set it, point it to the system bundle:
openssl.cafile=/etc/ssl/certs/ca-certificates.crt
If you've explicitly configured openssl.cafile or openssl.capath to point to an outdated or incorrect CA file, ensure it's removed or updated to point to the system's ca-certificates.crt. Remember to restart your PHP-FPM service if you modified its php.ini.
sudo systemctl restart php8.2-fpm # Adjust version as needed
5.3. APT for Custom Repositories
If you're using apt with a custom repository that's served over HTTPS using your private CA, the system-wide update-ca-certificates should be sufficient. If you encounter issues like The certificate is NOT trusted., double-check the CA installation.
6. Troubleshooting (If the issue persists)
- Verify Certificate Chain: Ensure you've added the correct certificate(s) to the trust store. If the server sends an intermediate certificate, make sure its issuer's certificate (the root) is trusted.
The# Inspect your custom CA certificate details openssl x509 -in /usr/local/share/ca-certificates/extra/custom_ca.crt -text -noout | grep "Issuer:" openssl x509 -in /usr/local/share/ca-certificates/extra/custom_ca.crt -text -noout | grep "Subject:"Issuerof your server's certificate should match theSubjectof your intermediate CA. TheIssuerof your intermediate CA should match theSubjectof your Root CA. If the Root CA is self-signed, itsIssuerandSubjectwill be the same. - Check File Permissions: Ensure the
.crtfiles in/usr/local/share/ca-certificates/are readable by all users (e.g.,chmod 644). - Restart Services: Always restart applications or services that make network requests after updating CA certificates, as they might cache the old trust store.
- Debug with
SSL_CERT_DIR: Forcurlorwget, you can temporarily point to a specific directory to test:
If this works, it indicates the issue is with the system-wide update or an application's specific configuration.# Create a temporary directory and put your CA cert there mkdir /tmp/test-certs && cp custom_ca.crt /tmp/test-certs/ # Use curl with the temporary certs SSL_CERT_DIR=/tmp/test-certs curl https://your-untrusted-server.com/
While it's possible to bypass SSL/TLS certificate validation (e.g.,
curl -k,wget --no-check-certificate), this is highly discouraged in production environments. Disabling validation removes a critical security layer, making your connections vulnerable to man-in-the-middle attacks. Always aim to properly configure trusted CAs.
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.