DEV Community

Cover image for Leaked TLS Private Keys: Proving Compromise, Finding Every Affected Cert & Rotating Safely
Muhammad Ali Mughal
Muhammad Ali Mughal

Posted on

Leaked TLS Private Keys: Proving Compromise, Finding Every Affected Cert & Rotating Safely

One of the most-read security posts on DEV this week is about an embarrassing private key in Web PKI history. The story is entertaining. The situation behind it is not: a TLS private key ends up somewhere public, and every certificate that uses it lets an attacker in the right network position impersonate your site. In incident reviews I have worked through, the leak itself is rarely the hard part. The hard part is answering three questions quickly and correctly: is this really our key, which certificates use it, and how do we rotate without an outage?

I wrote this with help from AI tools and checked every command and claim myself against current OpenSSL behaviour and CA documentation. I build PixelValut, a set of free browser-based developer and network tools, and this post opens a new series on Web PKI and certificate forensics, starting with the incident nobody wants: a leaked key.

📚 Series Roadmap — Web PKI & Certificate Forensics in Practice

  • Part 1 (This Post): Leaked TLS Private Keys: Proving Compromise, Finding Every Affected Cert & Rotating Safely
  • Part 2: Reading X.509 Certificates by Hand: SANs, Key Usage, Chains & Intermediates
  • Part 3: Certificate Revocation in 2026: CRLs, the Decline of OCSP & Short-Lived Certificates
  • Part 4: Certificate Transparency Monitoring: Catching Unexpected Certificates for Your Domains
  • Part 5: ACME Automation Failures: Renewal Outages, Rate Limits & DNS-01 Pitfalls
  • Part 6: Mutual TLS for Internal APIs: Private CAs, Rotation & Pinning Trade-offs

Previous series: Browser-Side Security & Zero-Trust Forensics

What should you do when a TLS private key leaks?

Direct answer: Confirm the leaked key matches your certificate by comparing public-key hashes, find every certificate issued for that key through Certificate Transparency logs, deploy new certificates on a freshly generated key, then revoke the old certificates with reason keyCompromise. Finally, remove the key from wherever it leaked, including git history and images.

Step 1: Prove the key is yours, without guessing

A file called server.key in a public repository proves nothing on its own. What links a private key to a certificate is the public key. Compute the SHA-256 of the SubjectPublicKeyInfo (SPKI) from both sides and compare:

# From the leaked private key
openssl pkey -in leaked.key -pubout -outform DER | openssl dgst -sha256

# From the certificate your server presents right now
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform DER | openssl dgst -sha256
Enter fullscreen mode Exit fullscreen mode

If the two digests match, the key belongs to that certificate. Do this on an isolated machine; never paste a private key into any website, including online tools. Public material, such as the certificate itself, is fine to inspect: a quick look with an SSL certificate checker shows the issuer, SANs and expiry you will need for the next step.

If the digests do not match, you are not done. Check older certificates and other hostnames, because keys are often reused across renewals and services.

Step 2: Find every certificate that uses the key

Renewal tools can keep the same key for years. Certbot, for example, has a --reuse-key option, and many manual processes simply resubmit the old CSR. So the leaked key may sit behind certificates you have forgotten about.

Certificate Transparency (CT) logs record publicly trusted certificates, and crt.sh lets you search them by SPKI hash:

SPKI=$(openssl pkey -in leaked.key -pubout -outform DER | openssl dgst -sha256 | awk '{print $2}')
curl -s "https://crt.sh/?spkisha256=${SPKI}&output=json" \
  | jq -r '.[] | [.id, .not_before, .not_after, .name_value] | @tsv' | sort -u
Enter fullscreen mode Exit fullscreen mode

Every row is a certificate an attacker can use with that key until it expires or is revoked. Keep the list; it becomes your rotation and revocation checklist.

A heavy steel bank vault door with a combination dial, slightly open, representing a private key that has left its safe, AI-generated
AI-generated image.

Step 3: Rotate before you revoke

The order matters. Revoke first and clients that check revocation may start failing before the new certificate is live.

# 1. New key (never reuse the leaked one)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out new.key

# 2. Issue a new certificate on the new key (ACME example)
certbot certonly --key-type ecdsa --new-key -d example.com -d www.example.com

# 3. Deploy and reload, then confirm the served SPKI changed
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256
Enter fullscreen mode Exit fullscreen mode

Check every endpoint from Step 2: load balancers, CDNs, mail servers and anything that terminated TLS with a copy of the key.

Step 4: Revoke with the right reason

Revoke each affected certificate with reason keyCompromise. The CA/Browser Forum Baseline Requirements require CAs to revoke within 24 hours once key compromise is demonstrated, and some CAs, including Let's Encrypt, also block the compromised key from future issuance.

# Let's Encrypt via certbot, signing the request with the compromised key itself
certbot revoke --cert-path old_cert.pem --key-path leaked.key --reason keycompromise
Enter fullscreen mode Exit fullscreen mode

For certificates from a commercial CA, use its revocation portal or API and select key compromise. If a third party leaked the key and you cannot reach the certificate owner, most CAs accept a key compromise report with proof of possession.

Be realistic about what revocation achieves. Browser revocation checking is limited, so revocation reduces risk; it does not make the old certificate harmless. That is why short lifetimes matter, and Part 3 of this series covers them.

Step 5: Remove the leak itself

# Find key material in a repository's full history
gitleaks detect --source . --log-opts="--all"

# Check container images, which keep deleted files in earlier layers
trufflehog docker --image=myapp:latest
Enter fullscreen mode Exit fullscreen mode

Rewrite history only after rotation, and assume clones and caches already exist. Rewriting history hides the key from new readers; it does not un-leak it.

Where keys leak, and what to fix

Leak source How it happens Detection Prevention
Git repository Key committed with config Secret scanning in CI .gitignore, pre-commit hooks
Container image COPY . . includes key Layer scan Mount secrets at runtime
Firmware / appliance Same key on every device Public research, CT Per-device keys
Support bundle / logs Debug dump includes PEM DLP scan Redact before upload
Backups / buckets Public bucket ACL Cloud config scan Private by default, encryption
Shared hosting panel Key copied between sites Manual audit One key per service

Response timeline

Time Action Owner
0–1 h Confirm SPKI match, list affected certs from CT Security on-call
1–4 h Issue new certs on new keys, deploy everywhere Platform team
4–24 h Revoke with keyCompromise, verify status Security
24–72 h Purge the leak source, rotate any related secrets Repo owners
1 week Postmortem, add secret scanning and key inventory Engineering leads

Diagnostic checklist

  1. Do the SPKI SHA-256 hashes of the leaked key and the live certificate match?
  2. Did you search CT logs by SPKI hash, not only by domain name?
  3. Was the new certificate issued on a newly generated key?
  4. Does every endpoint now serve the new SPKI?
  5. Were all affected certificates revoked with reason keyCompromise?
  6. Was the key removed from git history, images, backups and tickets?
  7. Were other secrets in the same leak (API keys, database passwords) rotated too?
  8. Is key reuse across renewals now disabled?
  9. Is secret scanning running on every push?
  10. Do you keep an inventory of which key serves which hostname?

FAQ

Can an attacker decrypt past traffic with the leaked key?

With modern TLS using ephemeral (ECDHE) key exchange, a leaked certificate key does not decrypt previously recorded sessions. It does allow impersonation of your server until the certificate stops being trusted.

Is it safe to check a private key in an online tool?

No. Treat any service that receives the key as another leak. Compare hashes locally with OpenSSL and only use online tools for public certificates.

Does a wildcard certificate change the response?

It widens the impact, because one key covers every subdomain. Rotate and revoke it the same way, and consider separate certificates for high-value hosts afterwards.

What if the leaked key belongs to a vendor appliance?

Report it to the vendor and the issuing CA, replace the certificate with your own key if the device allows it, and restrict access to the device until it is fixed.

How fast will the CA revoke?

The Baseline Requirements set a 24-hour window for demonstrated key compromise. Revoking it yourself with the key is usually immediate.

Should I also change HSTS or pinning settings?

HSTS does not need to change. If you pin public keys in a mobile app, ship the new key's pin before revoking, which is one reason Part 6 argues for pinning a backup key.

Next in this series, Part 2 reads an X.509 certificate field by field, so you can spot wrong SANs, missing intermediates and bad key usage before they become incidents.


Follow & Connect

Top comments (0)