What this skill does
Accepts an X.509 certificate file or PEM text and uses openssl x509 to parse the Subject, Issuer, SAN, validity period, public key info, and extension fields. If a CA bundle is provided, performs chain verification with openssl verify, and automatically checks for common issues such as weak keys, expiry, and self-signing.
When to use
- Inspecting a server certificate before or after deployment
- Debugging certificate chain errors (e.g., UNABLE_TO_GET_ISSUER_CERT)
- Checking certificate-related vulnerabilities (weak keys, wildcard misuse, etc.) during bug bounty or penetration testing
Prerequisites
openssl installed (built into Linux/macOS)
- Certificate file (PEM/DER) or PEM text
- CA bundle file for chain verification (optional)
Inputs
| Variable |
Description |
Example |
CERT_INPUT |
PEM file path or remote host |
/path/to/cert.pem or example.com:443 |
CA_BUNDLE |
(optional) CA bundle file path |
/etc/ssl/certs/ca-certificates.crt |
Workflow
Step 1: Extract PEM from certificate source
CERT_INPUT="/path/to/cert.pem" # or "example.com:443"
WORK_CERT="/tmp/target_cert.pem"
# Determine whether input is a file or host:port
if [ -f "$CERT_INPUT" ]; then
echo "=== Loading certificate from file: $CERT_INPUT ==="
# Auto-convert DER format to PEM
if openssl x509 -in "$CERT_INPUT" -noout 2>/dev/null; then
cp "$CERT_INPUT" "$WORK_CERT"
echo "PEM format confirmed"
else
openssl x509 -in "$CERT_INPUT" -inform DER -out "$WORK_CERT" 2>/dev/null && \
echo "DER -> PEM conversion complete" || echo "Certificate format parsing failed"
fi
else
echo "=== Fetching certificate from remote host: $CERT_INPUT ==="
HOST=$(echo "$CERT_INPUT" | cut -d: -f1)
PORT=$(echo "$CERT_INPUT" | cut -d: -f2)
PORT=${PORT:-443}
echo | openssl s_client \
-connect "${HOST}:${PORT}" \
-servername "$HOST" \
2>/dev/null | openssl x509 -out "$WORK_CERT"
echo "Remote certificate saved: $WORK_CERT"
fi
Step 2: Parse key certificate fields
echo ""
echo "=== Certificate details ==="
echo "--- Subject ---"
openssl x509 -in "$WORK_CERT" -noout -subject
echo ""
echo "--- Issuer ---"
openssl x509 -in "$WORK_CERT" -noout -issuer
echo ""
echo "--- Validity period ---"
openssl x509 -in "$WORK_CERT" -noout -dates
echo ""
echo "--- Subject Alternative Names (SAN) ---"
openssl x509 -in "$WORK_CERT" -noout -ext subjectAltName 2>/dev/null \
|| openssl x509 -in "$WORK_CERT" -text -noout | grep -A3 "Subject Alternative Name"
echo ""
echo "--- Public key info ---"
openssl x509 -in "$WORK_CERT" -noout -pubkey | openssl pkey -pubin -noout -text 2>/dev/null \
|| openssl x509 -in "$WORK_CERT" -noout -text | grep -A3 "Public Key Algorithm"
echo ""
echo "--- Signature algorithm ---"
openssl x509 -in "$WORK_CERT" -noout -text | grep "Signature Algorithm" | head -2
echo ""
echo "--- Extensions ---"
openssl x509 -in "$WORK_CERT" -noout -text \
| sed -n '/X509v3 extensions/,/Signature Algorithm/p' \
| head -40
Step 3: Automated security checks
echo ""
echo "=== Automated security checks ==="
# Check expiry
EXPIRY=$(openssl x509 -in "$WORK_CERT" -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -lt 0 ]; then
echo "[CRITICAL] Certificate expired (${DAYS_LEFT#-} days ago)"
elif [ "$DAYS_LEFT" -lt 30 ]; then
echo "[WARNING] Certificate expiring soon: ${DAYS_LEFT} days remaining"
else
echo "[OK] Certificate valid: ${DAYS_LEFT} days remaining"
fi
# Check for self-signed
SUBJECT=$(openssl x509 -in "$WORK_CERT" -noout -subject)
ISSUER=$(openssl x509 -in "$WORK_CERT" -noout -issuer)
if [ "$SUBJECT" = "$ISSUER" ]; then
echo "[WARNING] Self-signed certificate (Subject == Issuer)"
else
echo "[OK] CA-signed certificate"
fi
# Check key length
KEY_INFO=$(openssl x509 -in "$WORK_CERT" -noout -text 2>/dev/null | grep "Public-Key:")
KEY_BITS=$(echo "$KEY_INFO" | grep -oP '\(\K[0-9]+(?= bit)')
if [ -n "$KEY_BITS" ]; then
if [ "$KEY_BITS" -lt 2048 ]; then
echo "[CRITICAL] Weak RSA key: ${KEY_BITS} bits (minimum 2048 bits recommended)"
elif [ "$KEY_BITS" -lt 4096 ]; then
echo "[INFO] RSA key: ${KEY_BITS} bits (acceptable, 4096 bits recommended)"
else
echo "[OK] RSA key: ${KEY_BITS} bits"
fi
fi
# Check MD5/SHA1 signature algorithm
SIG_ALG=$(openssl x509 -in "$WORK_CERT" -noout -text | grep "Signature Algorithm" | head -1)
if echo "$SIG_ALG" | grep -qi "md5\|sha1"; then
echo "[CRITICAL] Weak signature algorithm: $SIG_ALG"
else
echo "[OK] Signature algorithm: $SIG_ALG"
fi
# Check for wildcard certificate
WILD=$(openssl x509 -in "$WORK_CERT" -noout -text | grep "\*\.")
if [ -n "$WILD" ]; then
echo "[INFO] Wildcard certificate detected: $WILD"
fi
Step 4: Certificate chain verification (when CA bundle is provided)
CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" # or custom CA bundle
echo ""
echo "=== Certificate chain verification ==="
if [ -f "$CA_BUNDLE" ]; then
openssl verify -CAfile "$CA_BUNDLE" "$WORK_CERT" 2>&1
else
echo "No CA bundle found. Attempting verification with system default CA..."
openssl verify "$WORK_CERT" 2>&1
fi
echo ""
echo "=== Full certificate text output (optional) ==="
echo "To print full output, run:"
echo " openssl x509 -in $WORK_CERT -text -noout"
Done when
- Subject, Issuer, SAN, validity period, public key algorithm, and signature algorithm are all printed
- Automated checks for expiry, self-signing, weak keys, and weak signature algorithms are complete
- (When CA bundle is provided) Chain verification result (OK or error code) is printed
Failure modes
| Symptom |
Cause and resolution |
unable to load certificate |
File format mismatch. If DER, add -inform DER |
| No SAN field |
Legacy certificate using CN only. Modern browsers may reject certificates without SAN |
Chain verification unable to get local issuer certificate |
Intermediate CA certificate missing. Fetch the full chain from the server |
| Remote certificate fetch failure |
Firewall or SNI issue. Check -servername flag and port |
Notes
- To fetch the full chain including intermediate CAs:
openssl s_client -connect host:443 -showcerts
- To extract the public key hash for certificate pinning verification:
openssl x509 -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | base64
- To search CT (Certificate Transparency) logs: https://crt.sh/?q=example.com