Public key infrastructure and certificates
In this series (16 parts)
- How attackers think: the attacker mindset
- Networking fundamentals for security
- Cryptography fundamentals
- Public key infrastructure and certificates
- Authentication and authorization
- Web application security: OWASP Top 10
- Network attacks and defenses
- Linux privilege escalation
- Windows security fundamentals
- Malware types and analysis basics
- Reconnaissance and OSINT
- Exploitation basics and CVEs
- Post-exploitation and persistence
- Defensive security: hardening and monitoring
- Incident response
- CTF skills and practice labs
Every HTTPS connection relies on certificates to prove the server is who it claims to be. Without certificates, encryption alone is not enough. You could have a perfectly encrypted connection to an attacker’s server. PKI (Public Key Infrastructure) solves this by creating a chain of trust from a small set of trusted authorities down to individual websites.
Prerequisites
You should understand cryptography fundamentals, specifically asymmetric encryption and digital signatures.
What is a certificate?
An X.509 certificate is a digitally signed document that binds a public key to an identity (like a domain name). It contains:
- Subject: who the certificate belongs to (e.g.,
CN=example.com) - Issuer: who signed it (e.g.,
CN=DigiCert Global G2) - Public key: the server’s public key
- Validity period: not before / not after dates
- Serial number: unique identifier
- Signature: the issuer’s digital signature over all the above
The chain of trust
graph TD A["Root CA<br/>Self-signed, stored in your OS/browser<br/>e.g., DigiCert Global Root G2"] -->|Signs| B["Intermediate CA<br/>Signed by Root CA<br/>e.g., DigiCert G2 TLS RSA SHA256"] B -->|Signs| C["Server Certificate<br/>Signed by Intermediate CA<br/>e.g., CN=example.com"] style A fill:#f9a825,stroke:#f57f17,color:#000 style B fill:#64b5f6,stroke:#1976d2,color:#000 style C fill:#81c784,stroke:#388e3c,color:#000
- Root CA is self-signed and pre-installed in your operating system or browser. You trust it because your OS vendor or browser vendor vetted it.
- Intermediate CA is signed by the Root CA. This adds a layer of protection. If the intermediate is compromised, the root can revoke it without replacing every certificate.
- Server certificate is signed by the Intermediate CA. This is what the web server presents.
When your browser connects to a site, it:
- Receives the server certificate and any intermediates
- Verifies the signature on the server cert using the intermediate’s public key
- Verifies the signature on the intermediate using the root’s public key
- Checks if the root is in its trusted store
- If the chain is valid and the domain matches, the connection is trusted
Certificate fields to inspect
# Inspect a website's certificate
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | head -40
Output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0a:bc:de:f0:12:34:56:78:90:ab:cd:ef:01:23:45:67
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
Validity
Not Before: Jan 13 00:00:00 2024 GMT
Not After : Feb 13 23:59:59 2025 GMT
Subject: CN = example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Key things to check:
- Subject / SAN: does the domain match what you expected?
- Issuer: is it a trusted CA?
- Validity: is it expired?
- Key size: RSA should be at least 2048 bits
- Signature algorithm: should be SHA-256 or better (not SHA-1 or MD5)
Common certificate errors
| Error | Meaning | Security risk |
|---|---|---|
NET::ERR_CERT_DATE_INVALID | Certificate expired | Could be misconfiguration or a revoked cert being reused |
NET::ERR_CERT_AUTHORITY_INVALID | Issuer not trusted | Self-signed cert, or MITM attack |
NET::ERR_CERT_COMMON_NAME_INVALID | Domain doesn’t match | Wrong cert installed, or MITM |
NET::ERR_CERT_REVOKED | Certificate was revoked | CA determined the cert was compromised |
SSL_ERROR_WEAK_SERVER_CERT_KEY | Key too small | RSA < 2048 bits |
⚠ Never tell users to “just click through” certificate warnings. Each warning could indicate an active attack.
Certificate transparency
CT is a system of public logs where all certificates are recorded. This means:
- Domain owners can monitor for unauthorized certificates issued for their domain
- Researchers can detect mis-issued certificates
- CAs are held accountable
# Check certificate transparency logs
curl -s "https://crt.sh/?q=example.com&output=json" | python3 -m json.tool | head -30
Let’s Encrypt and ACME
Let’s Encrypt provides free, automated certificates. The ACME protocol automates the entire process:
# Install certbot
sudo apt install -y certbot python3-certbot-nginx
# Get a certificate for your domain
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renewal is set up automatically
sudo certbot renew --dry-run
Let’s Encrypt certificates are valid for 90 days. The short validity period limits the damage from compromised certificates and encourages automation.
Example 1: Inspect a real certificate with OpenSSL
Let’s inspect Google’s certificate and understand every field:
# Download and display the certificate
echo | openssl s_client -connect google.com:443 -servername google.com 2>/dev/null | openssl x509 -noout -text
Extract specific fields:
# Just the subject
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -subject
Output:
subject=CN = *.google.com
# Validity dates
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -dates
Output:
notBefore=May 13 08:38:47 2026 GMT
notAfter=Aug 5 08:38:46 2026 GMT
# The issuer (who signed it)
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -issuer
Output:
issuer=C = US, O = Google Trust Services, CN = WR2
# Subject Alternative Names (all domains covered)
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -ext subjectAltName
Output:
X509v3 Subject Alternative Name:
DNS:*.google.com, DNS:*.appengine.google.com, DNS:*.bdn.dev, DNS:*.cloud.google.com, ...
# Check certificate fingerprint
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -fingerprint -sha256
Output:
sha256 Fingerprint=AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:...
Example 2: Trace the chain of trust for a public website
# Show the full certificate chain
echo | openssl s_client -connect google.com:443 -servername google.com -showcerts 2>/dev/null
This shows multiple certificates. Let’s extract and verify the chain:
# Save the chain
echo | openssl s_client -connect google.com:443 -showcerts 2>/dev/null > /tmp/chain.pem
# Extract each certificate
csplit -f /tmp/cert- -b '%02d.pem' /tmp/chain.pem '/-----BEGIN CERTIFICATE-----/' '{*}' 2>/dev/null
# Inspect each level
for cert in /tmp/cert-*.pem; do
if openssl x509 -in "$cert" -noout 2>/dev/null; then
echo "=== $(basename $cert) ==="
openssl x509 -in "$cert" -noout -subject -issuer
echo ""
fi
done
Output:
=== cert-01.pem ===
subject=CN = *.google.com
issuer=C = US, O = Google Trust Services, CN = WR2
=== cert-02.pem ===
subject=C = US, O = Google Trust Services, CN = WR2
issuer=C = US, O = Google Trust Services LLC, CN = GTS Root R1
The chain:
*.google.comsigned byWR2(Intermediate CA)WR2signed byGTS Root R1(Root CA)GTS Root R1is in your system’s trusted certificate store
Verify the chain:
# This checks the entire chain against the system's CA bundle
openssl verify -verbose /tmp/cert-01.pem
Output:
/tmp/cert-01.pem: OK
If the chain were broken or a certificate expired:
error 10 at 1 depth lookup: certificate has expired
Clean up:
rm -f /tmp/chain.pem /tmp/cert-*.pem
TLS/SSL versions
| Version | Status | Notes |
|---|---|---|
| SSL 2.0 | Broken | Never use |
| SSL 3.0 | Broken | POODLE attack |
| TLS 1.0 | Deprecated | Should be disabled |
| TLS 1.1 | Deprecated | Should be disabled |
| TLS 1.2 | Secure | Widely supported, still safe |
| TLS 1.3 | Secure | Preferred, faster handshake |
Check what a server supports:
# Test TLS 1.3
openssl s_client -connect example.com:443 -tls1_3 2>&1 | head -5
# Test TLS 1.2
openssl s_client -connect example.com:443 -tls1_2 2>&1 | head -5
What comes next
The next article covers Authentication and authorization, where you will learn about password hashing, MFA, OAuth, and JWT tokens.
For a deeper understanding of the math behind digital signatures, see probability fundamentals.