OpenSSL CLI: Debugging Production HTTPS and Building a Private CA

Security tutorial - IT technology blog
Security tutorial - IT technology blog

The 2:14 AM Production Alert

At 2:14 AM, my phone vibrated off the nightstand. The load balancer was throwing 502 Bad Gateway errors, and the logs were flooded with SSL_ERROR_BAD_CERT_DOMAIN. The frontend team swore they updated the certificates, but the browser disagreed. In these moments, your UI dashboard is often lying to you. You need the raw data that only the OpenSSL CLI provides.

After dealing with a series of SSH brute-force attempts last month, I’ve stopped taking “it should work” for granted. I verify everything. OpenSSL doesn’t care about your configuration scripts or what the management console says. It shows you exactly what the server presents during the handshake, making it the ultimate tool for cutting through the noise.

Three Reasons Certificates Fail

Most connection failures boil down to three culprits: an expired certificate, a mismatch between the private key and the public cert, or a missing intermediate chain. I often see junior admins copy the primary certificate but skip the bundle. This works in some browsers that cache intermediates but fails miserably for mobile users or API clients.

Microservices make this messier. When you route traffic through Nginx, HAProxy, and Traefik across 20 different subdomains, one wrong .pem file can trigger a “Your connection is not private” warning for thousands of users. A single typo in a path can bring down a production gateway.

Choosing the Right Debugging Tool

Online scanners like SSL Labs are great, but they can’t see behind your firewall. If you’re debugging a staging environment or an internal API tucked behind a VPN, those tools are useless. You could try curl -v, but it often suppresses the specific reason a handshake failed.

Using the OpenSSL CLI directly is the most reliable method. It allows you to simulate a real browser connection, inspect files before they are deployed, and even function as your own Certificate Authority (CA) for internal labs.

Phase 1: Debugging Live Connections

When a site goes dark, start by seeing what the server is actually sending. The s_client command is the go-to tool here. Don’t forget the Server Name Indication (SNI) flag. If your server hosts multiple sites on one IP, the server won’t know which certificate to show without the -servername flag.

# Check a remote server's certificate
openssl s_client -connect itfromzero.com:443 -servername itfromzero.com

Scan the output for the “Certificate chain” and “Verification” sections. If you see “Verification error: unable to get local issuer certificate,” your server is missing the intermediate bundle. To skip the wall of text and just see the expiration dates, pipe the output into the x509 utility:

# Extract just the validity dates
openssl s_client -connect google.com:443 -servername google.com 2>/dev/null | openssl x509 -noout -dates

Phase 2: Verifying Files Before Deployment

Before you reload Nginx and risk a production outage, verify your local files. I’ve seen engineers spend four hours debugging config files when the issue was simply a mismatched keypair. If the certificate doesn’t match the private key, the web server will either fail to start or reject every handshake attempt.

Inspecting Certificate Metadata

To confirm who issued the certificate and check the Subject Alternative Names (SANs), run this:

openssl x509 -in server.crt -text -noout

The Modulus Match Test

This is a mandatory check before every deployment. The public key inside the certificate must match the private key perfectly. You can verify this by comparing the MD5 hashes of their “modulus.” If these two strings aren’t identical, do not deploy them.

# Check the certificate modulus hash
openssl x509 -noout -modulus -in server.crt | openssl md5

# Check the private key modulus hash
openssl rsa -noout -modulus -in server.key | openssl md5

Phase 3: Building a Private CA for Internal Labs

Stop clicking “Advanced -> Proceed” on self-signed certificate warnings in your dev environment. The professional approach is building a Private CA. You create one Root certificate, trust it on your team’s machines, and then sign as many certificates as you need for local testing.

1. Generate the Root CA

First, create the master key and the root certificate. Treat this key like your production passwords; if someone steals it, they can spoof any site on your network.

# Create a 4096-bit RSA key
openssl genrsa -out MyInternalCA.key 4096

# Create the Root Certificate (Valid for 10 years)
openssl req -x509 -new -nodes -key MyInternalCA.key -sha256 -days 3650 -out MyInternalCA.pem

2. Create a Service Certificate

If you have a local site at api.dev.local, you’ll need a private key and a Certificate Signing Request (CSR).

# Generate the service key
openssl genrsa -out api.dev.local.key 2048

# Generate the CSR
openssl req -new -key api.dev.local.key -out api.dev.local.csr

3. Sign the Request with Your CA

Modern browsers like Chrome and Safari require a Subject Alternative Name (SAN) to trust a certificate. Create a small config file named dev.ext to include this.

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = api.dev.local

Now, sign the certificate. Note that I set the limit to 825 days, as macOS and iOS will reject any certificate with a longer lifespan.

openssl x509 -req -in api.dev.local.csr -CA MyInternalCA.pem -CAkey MyInternalCA.key \
-CAcreateserial -out api.dev.local.crt -days 825 -sha256 -extfile dev.ext

Import MyInternalCA.pem into your OS trust store. Once done, api.dev.local will show a clean green lock icon, mirroring your production environment exactly.

Best Practices for the Long Haul

OpenSSL doesn’t have to be a confusing mess of cryptic flags. If you follow a systematic workflow, you can solve almost any TLS issue in minutes.

Always check the chain of trust with s_client, verify the modulus match before every reload, and use a Private CA for your team to avoid “certificate warning fatigue.”

Security is a habit. Whether you are hardening SSH or ensuring your TLS handshakes are flawless, the tools you master define the reliability of your infrastructure. Get comfortable with the CLI, and those 2 AM alerts will become much easier to handle.

Share: