Introduction

When a browser displays Your connection is not secure or NET::ERR_CERT_AUTHORITY_INVALID, it means the certificate validation process has failed. The root cause can range from self-signed certificates to missing intermediates, expired root CAs, or domain mismatches. Identifying exactly why the browser doesn't trust your certificate is the first step toward fixing it.

Symptoms

  • Browser shows NET::ERR_CERT_AUTHORITY_INVALID or similar warning
  • Different browsers show different trust errors for the same site
  • Mobile devices fail while desktop browsers work (or vice versa)
  • curl returns SSL certificate problem: unable to get local issuer certificate
  • Internal applications fail with certificate verification errors
  • SSL Labs shows validation failures in the certification paths section

Common Causes

  • Self-signed certificate not added to trust store
  • Intermediate certificate missing from chain
  • Certificate issued by non-standard or internal CA
  • CA root not in client's trust store (old or obscure CA)
  • Certificate hostname doesn't match request URL
  • Certificate has been revoked by the CA
  • System clock on client is wrong

Step-by-Step Fix

Step 1: Determine the Trust Failure Type

Run a comprehensive certificate check:

```bash # Check certificate details openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text

# Check verification result echo | openssl s_client -connect example.com:443 -servername example.com 2>&1 | grep -E "Verify return|depth|error"

# Get full chain info echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -E "s:|i:|Verify" ```

Review the output for these key indicators:

bash
# Look for these error messages
echo | openssl s_client -connect example.com:443 2>&1 | grep -i "error\|alert"

Step 2: Check if Self-Signed or Private CA

```bash # Check issuer - if issuer equals subject, it's self-signed openssl x509 -in /path/to/cert.pem -noout -subject -issuer

# Self-signed will show something like: # subject=CN = example.com # issuer=CN = example.com

# Private CA will show your organization's CA name ```

If self-signed or private CA, you need to add it to client trust stores:

```bash # For Linux clients sudo cp ca-cert.pem /usr/local/share/ca-certificates/ sudo update-ca-certificates

# For Windows (via PowerShell as Admin) Import-Certificate -FilePath "ca-cert.pem" -CertStoreLocation Cert:\LocalMachine\Root

# For macOS sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca-cert.pem ```

Step 3: Fix Missing Intermediate Chain

```bash # Check how many certs are being served echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -c "BEGIN CERTIFICATE"

# Should be 2+ (server cert + intermediate(s)) # If only 1, you're missing intermediates

# Get intermediate from your CA or use certbot's fullchain # For Let's Encrypt, use the fullchain.pem file ls -la /etc/letsencrypt/live/example.com/ # Should see: cert.pem, chain.pem, fullchain.pem, privkey.pem ```

Configure your web server to use the full chain:

nginx
# Nginx - use fullchain.pem, not cert.pem
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Step 4: Verify Domain Matching

```bash # Check Subject Alternative Names openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text | grep -A 1 "Subject Alternative Name"

# Check Common Name openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -subject ```

The domain accessed must match one of the SANs or the CN exactly.

Step 5: Check for Revocation

```bash # Get OCSP URL from certificate openssl x509 -in cert.pem -noout -ocsp_uri

# Check revocation via OCSP openssl ocsp -issuer chain.pem -cert cert.pem -url $(openssl x509 -in cert.pem -noout -ocsp_uri) -resp_text

# Or check CRL openssl x509 -in cert.pem -noout -text | grep -A 4 "CRL Distribution Points" ```

Step 6: Comprehensive Verification

```bash # Full verification against system trust store openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt fullchain.pem

# With specific host verification openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt -verify_hostname example.com fullchain.pem

# Test actual connection curl -vI https://example.com 2>&1 | grep -E "SSL certificate verify|subject:|issuer:" ```

Common Pitfalls

  • Using cert.pem instead of fullchain.pem in Nginx config
  • Forgetting to add intermediate to Java keystore separately
  • Testing on the same machine where you added the cert to trust store
  • Not realizing different browsers/devices have different root stores
  • Certificate works in Chrome but fails in Firefox (different trust stores)

Best Practices

  • Always use certificates from trusted public CAs for public-facing services
  • Use fullchain certificate bundles in server configuration
  • Test with SSL Labs after certificate changes
  • Set up certificate monitoring that checks trust chain validity
  • Document certificate renewal process and CA relationships
  • For internal services, establish a private CA and distribute root cert properly
  • SSL Certificate Chain Incomplete
  • SSL Certificate Name Mismatch
  • SSL Certificate Expired
  • SSL Self-Signed Certificate