When implementing mutual TLS authentication with intermediate CAs, a common pain point emerges when Nginx returns a generic "400 The SSL certificate error" without clear diagnostic information. The root cause typically lies in certificate chain validation issues between client and server trust stores.
From the debug output, we observe the server expects client certificates signed by:
/C=GB/ST=England/L=Sydney/O=Something/OU=Shared Services/CN=client-and-ca.test.com
But the actual client certificate chain shows a different trust path. This mismatch triggers the 400 error.
The nginx configuration needs these critical adjustments:
server {
# ... other SSL configurations ...
# Must include the full chain of trust for client certs
ssl_client_certificate /path/to/client_rootca.crt;
ssl_client_certificate /path/to/client_intermediate.crt;
# Verification depth should account for intermediate CAs
ssl_verify_depth 3;
}
For OpenSSL clients, the certificate bundle must include:
cat client.crt intermediate.crt root.crt > client_bundle.pem
openssl s_client -connect server:443 \
-cert client_bundle.pem \
-key client.key \
-CAfile server_trust_chain.pem
Enable detailed logging in nginx:
error_log /var/log/nginx/ssl_error.log debug;
ssl_crl /path/to/crl.pem; # If using certificate revocation
Key OpenSSL verification commands:
openssl verify -CAfile root.crt -untrusted intermediate.crt client.crt
openssl x509 -in cert.pem -text -noout
- Missing intermediate certificates in the chain
- Mismatched DN requirements between client and server
- Incorrect verification depth settings
- Clock skew causing certificate validity period issues
For enterprise deployments:
# OCSP stapling configuration
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/trusted_chain.crt;
The error occurs when Nginx configured for mutual SSL authentication rejects client certificates during the TLS handshake. From the logs, we can see the server returns "400 The SSL certificate error" specifically during client certificate verification phase.
Your current Nginx configuration shows:
ssl_client_certificate /root/client_rootca.crt;
ssl_verify_client on;
The critical missing piece is the complete certificate chain trust configuration. When using intermediate CAs, you must:
- Bundle all relevant CA certificates
- Ensure proper certificate chain order
- Validate the trust chain configuration
First, create a proper CA bundle file containing all necessary certificates:
# Create combined CA file
cat /root/ca/intermediate/certs/intermediate.cert.pem \\
/root/ca/certs/ca.cert.pem \\
> /etc/nginx/ssl/client_ca_bundle.crt
Then update your Nginx configuration:
server {
listen 443 ssl;
server_name app-ca.test.com;
ssl_certificate /root/ca/intermediate/certs/app-plus-intermediate.pem;
ssl_certificate_key /root/ca/intermediate/private/app-ca-interm-ca.test.com.key.pem;
# Updated client certificate verification
ssl_client_certificate /etc/nginx/ssl/client_ca_bundle.crt;
ssl_verify_client optional_no_ca;
# For debugging
error_log /var/log/nginx/ssl_error.log debug;
location / {
if ($ssl_client_verify != SUCCESS) {
return 403 "Client certificate verification failed: $ssl_client_verify";
}
root /usr/share/nginx/massl;
index index.html index.htm;
}
}
Use OpenSSL to verify the complete chain:
openssl s_client -connect app-ca.test.com:443 \\
-key /root/ca/intermediate/private/client.key.pem \\
-cert /root/ca/intermediate/certs/client_plus_intermediate.cert.pem \\
-CAfile /root/server_rootca.crt \\
-state -debug -tlsextdebug
Problem: Intermediate CA not trusted
Solution: Ensure your CA bundle file includes all intermediate certificates in the correct order (client intermediate first, then root CA)
Problem: Certificate subject mismatch
Solution: Verify the CN (Common Name) or SAN (Subject Alternative Name) in your client certificate matches what the server expects
# Check certificate details
openssl x509 -in client.cert.pem -text -noout
For production environments, consider adding these enhancements:
ssl_verify_depth 3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_crl /path/to/certificate_revocation_list.crl;
Use these commands to diagnose certificate chain issues:
# Verify certificate chain
openssl verify -CAfile server_rootca.crt -untrusted intermediate.cert.pem client.cert.pem
# Check TLS handshake
curl -v --cert client.cert.pem --key client.key.pem https://app-ca.test.com