DevSecOps

SSL Certificate Errors: Complete Troubleshooting Guide

DeviDevs Team
6 min read
#ssl#tls#certificates#https#troubleshooting

SSL/TLS certificate errors can break applications and block deployments. This guide covers all common certificate issues and their solutions.

Error: Certificate Has Expired

Symptom:

curl: (60) SSL certificate problem: certificate has expired
javax.net.ssl.SSLHandshakeException: Certificate expired
NET::ERR_CERT_DATE_INVALID

Solution 1 - Check expiration:

# Check certificate expiration
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
 
# Or using curl
curl -vI https://example.com 2>&1 | grep -i "expire"
 
# Check local certificate file
openssl x509 -enddate -noout -in /path/to/cert.pem

Solution 2 - Renew with Let's Encrypt:

# Using certbot
sudo certbot renew
 
# Force renewal
sudo certbot renew --force-renewal
 
# Dry run test
sudo certbot renew --dry-run
 
# After renewal, restart services
sudo systemctl reload nginx

Solution 3 - Auto-renewal setup:

# Cron job for auto-renewal
echo "0 0,12 * * * root certbot renew --quiet" | sudo tee /etc/cron.d/certbot
 
# Systemd timer (preferred)
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Error: Self-Signed Certificate Not Trusted

Symptom:

SSL certificate problem: self-signed certificate
unable to verify the first certificate
DEPTH_ZERO_SELF_SIGNED_CERT

Solution 1 - Add CA to trust store:

# Linux (Ubuntu/Debian)
sudo cp custom-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
 
# Linux (CentOS/RHEL)
sudo cp custom-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
 
# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain custom-ca.crt

Solution 2 - Configure application:

# curl - specify CA bundle
curl --cacert /path/to/ca-bundle.crt https://internal-server.local
 
# Node.js
export NODE_EXTRA_CA_CERTS=/path/to/ca.pem
 
# Python requests
export REQUESTS_CA_BUNDLE=/path/to/ca-bundle.crt
# Python with custom CA
import requests
response = requests.get('https://internal.local', verify='/path/to/ca.crt')
 
# Or disable verification (NOT for production!)
response = requests.get('https://internal.local', verify=False)

Solution 3 - Docker with custom CA:

FROM python:3.12
 
# Add custom CA certificate
COPY custom-ca.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
 
# Node.js needs explicit path
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/custom-ca.crt

Error: Hostname Mismatch

Symptom:

SSL: CERTIFICATE_VERIFY_FAILED certificate verify failed: Hostname mismatch
The certificate is not valid for the requested host
NET::ERR_CERT_COMMON_NAME_INVALID

Cause: Certificate issued for different domain.

Solution 1 - Check certificate domains:

# View certificate SANs (Subject Alternative Names)
openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
 
# Full certificate details
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -text

Solution 2 - Issue certificate with correct names:

# Let's Encrypt with multiple domains
sudo certbot certonly --nginx \
  -d example.com \
  -d www.example.com \
  -d api.example.com
 
# Self-signed with SAN
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \
  -nodes -keyout server.key -out server.crt \
  -subj "/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:*.example.com"

Error: Certificate Chain Incomplete

Symptom:

unable to get local issuer certificate
SSL certificate problem: unable to get issuer certificate
The certificate chain is incomplete

Solution 1 - Include intermediate certificates:

# Download intermediate cert from CA
wget https://letsencrypt.org/certs/lets-encrypt-r3.pem
 
# Concatenate in correct order
cat server.crt intermediate.crt > fullchain.crt
 
# Verify chain
openssl verify -CAfile ca-bundle.crt fullchain.crt

Solution 2 - Check chain completeness:

# Online tool
# https://www.ssllabs.com/ssltest/
 
# CLI check
openssl s_client -connect example.com:443 -showcerts
 
# You should see multiple certificates in the output

Solution 3 - Nginx configuration:

server {
    listen 443 ssl;
    server_name example.com;
 
    # Full chain, not just server cert
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

Error: TLS Version Not Supported

Symptom:

SSL routines:ssl3_get_record:wrong version number
tlsv1 alert protocol version
sslv3 alert handshake failure

Solution 1 - Check supported TLS versions:

# Test TLS 1.2
openssl s_client -connect example.com:443 -tls1_2
 
# Test TLS 1.3
openssl s_client -connect example.com:443 -tls1_3
 
# See negotiated protocol
curl -v https://example.com 2>&1 | grep "SSL connection"

Solution 2 - Configure server for modern TLS:

# Nginx - TLS 1.2 and 1.3 only
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

Solution 3 - Client configuration:

# Python - force TLS 1.2+
import ssl
import urllib.request
 
ctx = ssl.create_default_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
 
response = urllib.request.urlopen('https://example.com', context=ctx)
// Node.js
const https = require('https');
const agent = new https.Agent({
  minVersion: 'TLSv1.2'
});

Error: Certificate Revoked

Symptom:

certificate revoked
NET::ERR_CERT_REVOKED
SSL_ERROR_REVOKED_CERT_ALERT

Solution 1 - Check revocation status:

# Get OCSP responder URL
openssl x509 -in cert.pem -noout -ocsp_uri
 
# Check OCSP status
openssl ocsp -issuer chain.pem -cert cert.pem \
  -url http://ocsp.example.com -resp_text

Solution 2 - Issue new certificate:

# Revoked certificates cannot be un-revoked
# Generate new CSR and request new certificate
openssl req -new -key server.key -out new-server.csr
 
# Request from CA or Let's Encrypt
sudo certbot certonly --nginx -d example.com

Error: Private Key Mismatch

Symptom:

SSL_CTX_use_PrivateKey_file failed
key values mismatch
(SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)

Solution - Verify key matches certificate:

# Get modulus from certificate
openssl x509 -noout -modulus -in cert.pem | openssl md5
 
# Get modulus from private key
openssl rsa -noout -modulus -in key.pem | openssl md5
 
# These MD5 hashes must match!

Regenerate matching pair if needed:

# Generate new private key
openssl genrsa -out new-server.key 4096
 
# Create CSR
openssl req -new -key new-server.key -out new-server.csr
 
# Self-sign or submit CSR to CA
openssl x509 -req -days 365 -in new-server.csr \
  -signkey new-server.key -out new-server.crt

Common Service Configurations

Nginx:

server {
    listen 443 ssl http2;
    server_name example.com;
 
    ssl_certificate /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
 
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
 
    # HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;
}

Apache:

<VirtualHost *:443>
    ServerName example.com
 
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/server.crt
    SSLCertificateKeyFile /etc/ssl/private/server.key
    SSLCertificateChainFile /etc/ssl/certs/chain.crt
 
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    SSLHonorCipherOrder off
</VirtualHost>

Quick Reference: Debug Commands

| Task | Command | |------|---------| | Check expiry | openssl x509 -enddate -noout -in cert.pem | | View certificate | openssl x509 -text -noout -in cert.pem | | Test connection | openssl s_client -connect host:443 | | Verify chain | openssl verify -CAfile ca.crt cert.pem | | Check key match | openssl x509 -modulus -noout -in cert.pem \| md5 | | Download cert | echo \| openssl s_client -connect host:443 2>/dev/null \| openssl x509 > cert.pem |

SSL/TLS Security Consulting?

Certificate management at scale requires automation and expertise. Our team offers:

  • Certificate lifecycle automation
  • PKI infrastructure design
  • Zero-trust architecture implementation
  • Compliance auditing (PCI-DSS, HIPAA)

Get SSL security help

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.