OpenSSL Certificates
Certificate generation, inspection, and conversion with OpenSSL.
Certificate Fundamentals
# X.509 CERTIFICATE STRUCTURE
# - Version (v3 most common)
# - Serial Number (unique per CA)
# - Signature Algorithm (SHA256withRSA, etc.)
# - Issuer (CA that signed)
# - Validity (Not Before, Not After)
# - Subject (who cert is for)
# - Public Key
# - Extensions (SAN, Key Usage, etc.)
# - Signature (CA's signature over above)
# VIEW CERTIFICATE
openssl x509 -in cert.pem -noout -text
# Full details
# COMMON VIEW OPTIONS
openssl x509 -in cert.pem -noout -subject # Subject only
openssl x509 -in cert.pem -noout -issuer # Issuer only
openssl x509 -in cert.pem -noout -dates # Validity dates
openssl x509 -in cert.pem -noout -serial # Serial number
openssl x509 -in cert.pem -noout -fingerprint # SHA-1 fingerprint
openssl x509 -in cert.pem -noout -fingerprint -sha256 # SHA-256
# MULTIPLE FIELDS
openssl x509 -in cert.pem -noout -subject -issuer -dates
# SUBJECT/ISSUER FORMAT
openssl x509 -in cert.pem -noout -subject -nameopt RFC2253
# Output: CN=hostname,O=Organization,C=US
openssl x509 -in cert.pem -noout -subject -nameopt oneline
# Output: CN = hostname, O = Organization, C = US
# VIEW EXTENSIONS
openssl x509 -in cert.pem -noout -ext subjectAltName
openssl x509 -in cert.pem -noout -ext keyUsage
openssl x509 -in cert.pem -noout -ext extendedKeyUsage
# CERTIFICATE CHAIN
openssl crl2pkcs7 -nocrl -certfile chain.pem | \
openssl pkcs7 -print_certs -noout
# Lists all certs in chain
# COUNT CERTS IN CHAIN
grep -c 'BEGIN CERTIFICATE' chain.pem
Remote Certificate Inspection
# FETCH REMOTE CERTIFICATE
echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 -noout -subject -dates
# WITH SNI (Server Name Indication)
echo | openssl s_client -connect host:443 -servername host.example.com 2>/dev/null | \
openssl x509 -noout -text
# SAVE REMOTE CERTIFICATE
echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 > remote-cert.pem
# FETCH FULL CHAIN
echo | openssl s_client -connect host:443 -showcerts 2>/dev/null | \
awk '/BEGIN CERT/,/END CERT/' > chain.pem
# CONNECTION DETAILS
openssl s_client -connect host:443 -brief
# Output: CONNECTION ESTABLISHED
# Protocol: TLSv1.3
# Cipher: TLS_AES_256_GCM_SHA384
# TLS VERSION TESTING
openssl s_client -connect host:443 -tls1_2 # Force TLS 1.2
openssl s_client -connect host:443 -tls1_3 # Force TLS 1.3
# CHECK CERTIFICATE EXPIRY (one-liner)
echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2
# DAYS UNTIL EXPIRY
EXPIRY=$(echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "Days until expiry: $DAYS"
# INFRASTRUCTURE PATTERNS
# Check ISE certificate
echo | openssl s_client -connect ise-01.inside.domusdigitalis.dev:443 2>/dev/null | \
openssl x509 -noout -subject -issuer -dates
# Check Vault certificate
echo | openssl s_client -connect vault-01.inside.domusdigitalis.dev:8200 2>/dev/null | \
openssl x509 -noout -subject -dates
# Check multiple hosts
for host in ise-01 vault-01 grafana; do
FQDN="${host}.inside.domusdigitalis.dev"
EXPIRY=$(echo | openssl s_client -connect "$FQDN:443" 2>/dev/null | \
openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
echo "$host: $EXPIRY"
done
# Check certificate chain validity
echo | openssl s_client -connect host:443 -CAfile /etc/ssl/certs/ca-certificates.crt 2>&1 | \
grep "Verify return code"
# Output: Verify return code: 0 (ok)
Certificate Chain Verification
# VERIFY CERTIFICATE AGAINST CA
openssl verify -CAfile ca.pem cert.pem
# Output: cert.pem: OK
# VERIFY WITH INTERMEDIATE
openssl verify -CAfile root.pem -untrusted intermediate.pem cert.pem
# VERIFY CHAIN FILE
openssl verify -CAfile chain.pem cert.pem
# VERBOSE VERIFICATION
openssl verify -verbose -CAfile ca.pem cert.pem
# Shows full chain
# VERIFY PARTIAL CHAIN
openssl verify -partial_chain -CAfile intermediate.pem cert.pem
# OK if intermediate is trusted (even without root)
# CHECK CHAIN ORDER
# Correct order: leaf -> intermediate -> root
openssl crl2pkcs7 -nocrl -certfile chain.pem | \
openssl pkcs7 -print_certs -noout | \
grep -E "subject=|issuer="
# BUILD CORRECT CHAIN
cat leaf.pem intermediate.pem root.pem > full-chain.pem
# VERIFY KEY MATCHES CERTIFICATE
CERT_MOD=$(openssl x509 -in cert.pem -noout -modulus | sha256sum)
KEY_MOD=$(openssl rsa -in key.pem -noout -modulus | sha256sum)
[ "$CERT_MOD" = "$KEY_MOD" ] && echo "Match" || echo "MISMATCH!"
# FOR EC KEYS
CERT_PUB=$(openssl x509 -in cert.pem -noout -pubkey | sha256sum)
KEY_PUB=$(openssl ec -in key.pem -pubout 2>/dev/null | sha256sum)
[ "$CERT_PUB" = "$KEY_PUB" ] && echo "Match" || echo "MISMATCH!"
# INFRASTRUCTURE PATTERNS
# Verify EAP-TLS certificate chain
openssl verify \
-CAfile ~/.secrets/certs/d000/DOMUS-ROOT-CA.pem \
-untrusted ~/.secrets/certs/d000/DOMUS-ISSUING-CA.pem \
/etc/ssl/certs/modestus-razer-eaptls.pem
# Verify against system CA store
openssl verify -CApath /etc/ssl/certs/ cert.pem
# Verify ISE trusts our CA chain
# (via ISE API)
netapi ise api-call openapi GET '/api/v1/certs/trusted-certificate?size=100' | \
jq -r '.response[].friendlyName' | grep -i domus
# Check DOMUS PKI chain
echo "=== Root CA ==="
openssl x509 -in ~/.secrets/certs/d000/DOMUS-ROOT-CA.pem -noout -subject -dates
echo "=== Issuing CA ==="
openssl x509 -in ~/.secrets/certs/d000/DOMUS-ISSUING-CA.pem -noout -subject -issuer -dates
Vault PKI Certificate Issuance
# VAULT PKI OVERVIEW
# pki/ - Root CA (offline)
# pki_int/ - Issuing CA (issues certificates)
# LIST PKI ROLES
vault list pki_int/roles
# VIEW ROLE CONFIGURATION
vault read pki_int/roles/domus-client
# ISSUE CERTIFICATE
vault write pki_int/issue/domus-client \
common_name="hostname.inside.domusdigitalis.dev" \
ttl="8760h"
# Returns: certificate, issuing_ca, private_key, serial_number
# ISSUE WITH SAN
vault write pki_int/issue/domus-client \
common_name="hostname.inside.domusdigitalis.dev" \
alt_names="alias.inside.domusdigitalis.dev,another.example.com" \
ip_sans="10.50.1.100" \
ttl="8760h"
# SAVE TO FILES
vault write -format=json pki_int/issue/domus-client \
common_name="hostname.inside.domusdigitalis.dev" \
ttl="8760h" > /tmp/cert.json
jq -r '.data.certificate' /tmp/cert.json > /tmp/cert.pem
jq -r '.data.private_key' /tmp/cert.json > /tmp/key.pem
jq -r '.data.issuing_ca' /tmp/cert.json > /tmp/ca.pem
# VERIFY ISSUED CERTIFICATE
openssl x509 -in /tmp/cert.pem -noout -subject -issuer -dates
# LIST ISSUED CERTIFICATES
vault list pki_int/certs
# VIEW CERTIFICATE BY SERIAL
vault read pki_int/cert/12:34:56:78:90:ab:cd:ef
# REVOKE CERTIFICATE
vault write pki_int/revoke serial_number="12:34:56:78:90:ab:cd:ef"
# TIDY CERTIFICATES (cleanup expired)
vault write pki_int/tidy \
tidy_cert_store=true \
tidy_revoked_certs=true \
safety_buffer="72h"
# INFRASTRUCTURE PATTERNS
# Issue EAP-TLS certificate
HOSTNAME="modestus-razer"
vault write -format=json pki_int/issue/domus-client \
common_name="${HOSTNAME}.inside.domusdigitalis.dev" \
ttl="8760h" > /tmp/eaptls-cert.json
jq -r '.data.certificate' /tmp/eaptls-cert.json > /etc/ssl/certs/${HOSTNAME}-eaptls.pem
jq -r '.data.private_key' /tmp/eaptls-cert.json > /etc/ssl/private/${HOSTNAME}-eaptls.key
chmod 600 /etc/ssl/private/${HOSTNAME}-eaptls.key
# Issue server certificate (with SANs)
vault write -format=json pki_int/issue/domus-server \
common_name="grafana.inside.domusdigitalis.dev" \
alt_names="prometheus.inside.domusdigitalis.dev,alertmanager.inside.domusdigitalis.dev" \
ip_sans="10.50.1.130" \
ttl="8760h" > /tmp/server-cert.json
# Get CA chain for trust configuration
vault read -field=certificate pki/cert/ca > DOMUS-ROOT-CA.pem
vault read -field=certificate pki_int/cert/ca > DOMUS-ISSUING-CA.pem
cat DOMUS-ISSUING-CA.pem DOMUS-ROOT-CA.pem > DOMUS-CA-CHAIN.pem
SSH Certificates
# SSH CERTIFICATES VS KEYS
# Keys: public/private keypair, manual distribution
# Certs: signed by CA, automatic trust, expiry, principals
# VIEW SSH CERTIFICATE
ssh-keygen -Lf ~/.ssh/id_ed25519_vault-cert.pub
# Output:
# Type: ssh-ed25519-cert-v01@openssh.com
# Public key: SHA256:abc123...
# Signing CA: SHA256:def456...
# Key ID: "evan-domus"
# Serial: 1234567890
# Valid: from 2024-01-15T10:00:00 to 2024-01-15T18:00:00
# Principals:
# evanusmodestus
# admin
# root
# Critical Options: (none)
# Extensions:
# permit-pty
# VAULT SSH CA SIGNING
vault write -field=signed_key ssh/sign/domus-client \
public_key=@$HOME/.ssh/id_ed25519_vault.pub \
valid_principals="evanusmodestus,admin,root,ansible" \
> ~/.ssh/id_ed25519_vault-cert.pub
# CHECK CERTIFICATE VALIDITY
ssh-keygen -Lf ~/.ssh/id_ed25519_vault-cert.pub | grep -E "Valid:|Principals:"
# CHECK IF EXPIRED
VALID_TO=$(ssh-keygen -Lf ~/.ssh/id_ed25519_vault-cert.pub | grep "Valid:" | awk '{print $5}')
VALID_EPOCH=$(date -d "$VALID_TO" +%s 2>/dev/null || echo 0)
NOW_EPOCH=$(date +%s)
if [ $NOW_EPOCH -gt $VALID_EPOCH ]; then
echo "Certificate EXPIRED!"
else
HOURS_LEFT=$(( (VALID_EPOCH - NOW_EPOCH) / 3600 ))
echo "Valid for $HOURS_LEFT more hours"
fi
# SSH CA TRUST CONFIGURATION
# On SSH server (/etc/ssh/sshd_config):
# TrustedUserCAKeys /etc/ssh/vault-ca.pub
# Get CA public key from Vault
vault read -field=public_key ssh/config/ca > vault-ca.pub
# AUTHORIZED PRINCIPALS
# On SSH server (/etc/ssh/sshd_config):
# AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# Create principals file
echo "evanusmodestus" > /etc/ssh/auth_principals/evanusmodestus
echo -e "admin\nroot" >> /etc/ssh/auth_principals/evanusmodestus
# HOST CERTIFICATES
# Sign host key with CA
vault write ssh/sign/host-signer \
cert_type=host \
public_key=@/etc/ssh/ssh_host_ed25519_key.pub \
> /etc/ssh/ssh_host_ed25519_key-cert.pub
# INFRASTRUCTURE PATTERNS
# Re-sign SSH certificate (daily)
~/.local/bin/vault-ssh-sign
# Test connectivity
~/.local/bin/vault-ssh-test
# Debug SSH cert issues
ssh -vvv host.example.com 2>&1 | grep -E "certificate|principal|key"
Certificate Generation (Self-Signed & CSR)
# GENERATE SELF-SIGNED CERTIFICATE
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout key.pem \
-out cert.pem \
-subj "/CN=localhost"
# WITH EXTENSIONS (SAN)
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout key.pem \
-out cert.pem \
-subj "/CN=myserver.local" \
-addext "subjectAltName=DNS:myserver.local,DNS:localhost,IP:127.0.0.1"
# ELLIPTIC CURVE (faster, smaller)
openssl req -x509 -nodes -days 365 \
-newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout key.pem \
-out cert.pem \
-subj "/CN=myserver.local"
# GENERATE CSR (Certificate Signing Request)
openssl req -new \
-newkey rsa:2048 \
-nodes \
-keyout key.pem \
-out request.csr \
-subj "/CN=myserver.example.com/O=My Org/C=US"
# CSR WITH SAN (config file method)
cat > csr.conf << 'EOF'
[req]
distinguished_name = req_dn
req_extensions = req_ext
prompt = no
[req_dn]
CN = myserver.example.com
O = My Organization
C = US
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = myserver.example.com
DNS.2 = www.myserver.example.com
IP.1 = 10.0.0.100
EOF
openssl req -new -newkey rsa:2048 -nodes \
-keyout key.pem -out request.csr \
-config csr.conf
# VIEW CSR
openssl req -in request.csr -noout -text
# VERIFY CSR
openssl req -in request.csr -noout -verify
# Output: verify OK
# SIGN CSR WITH CA
openssl x509 -req \
-in request.csr \
-CA ca.pem \
-CAkey ca-key.pem \
-CAcreateserial \
-out signed-cert.pem \
-days 365 \
-extfile csr.conf \
-extensions req_ext
# GENERATE CA (for testing)
openssl req -x509 -nodes -days 3650 \
-newkey rsa:4096 \
-keyout ca-key.pem \
-out ca.pem \
-subj "/CN=Test CA/O=Test Org/C=US"
# PKCS#12 CONVERSION
# PEM to PKCS#12 (for Windows/browsers)
openssl pkcs12 -export \
-inkey key.pem \
-in cert.pem \
-certfile ca.pem \
-out cert.p12
# PKCS#12 to PEM
openssl pkcs12 -in cert.p12 -out extracted.pem -nodes
Certificate Format Conversion
# PEM (Base64, most common on Linux)
# -----BEGIN CERTIFICATE-----
# MIIBkTCB+wI...
# -----END CERTIFICATE-----
# DER (Binary, used by Java, Windows)
# Binary blob, no headers
# PKCS#12/PFX (Archive format, key + cert + chain)
# Binary, password protected
# PEM TO DER
openssl x509 -in cert.pem -outform DER -out cert.der
openssl rsa -in key.pem -outform DER -out key.der
# DER TO PEM
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
openssl rsa -in key.der -inform DER -outform PEM -out key.pem
# PEM TO PKCS#12
openssl pkcs12 -export \
-in cert.pem \
-inkey key.pem \
-certfile ca-chain.pem \
-out cert.p12 \
-name "My Certificate"
# Prompts for export password
# PKCS#12 TO PEM
openssl pkcs12 -in cert.p12 -out all.pem -nodes
# Contains key, cert, and chain in one file
# EXTRACT JUST CERTIFICATE
openssl pkcs12 -in cert.p12 -nokeys -out cert.pem
# EXTRACT JUST KEY
openssl pkcs12 -in cert.p12 -nocerts -nodes -out key.pem
# EXTRACT CA CERTIFICATES
openssl pkcs12 -in cert.p12 -cacerts -nokeys -out ca-chain.pem
# PKCS#7 (certificate chain, no key)
# PEM chain to PKCS#7
openssl crl2pkcs7 -nocrl -certfile chain.pem -out chain.p7b
# PKCS#7 to PEM
openssl pkcs7 -in chain.p7b -print_certs -out chain.pem
# VIEW PKCS#12 INFO
openssl pkcs12 -in cert.p12 -info -nokeys
# Shows certificate details without extracting
# VIEW PKCS#12 CONTENTS
openssl pkcs12 -in cert.p12 -info -nodes 2>/dev/null | \
grep -E "subject=|issuer=|friendlyName"
# INFRASTRUCTURE PATTERNS
# Export Vault cert as PKCS#12 for Windows
vault write -format=json pki_int/issue/domus-client \
common_name="winpc.inside.domusdigitalis.dev" \
ttl="8760h" > /tmp/win-cert.json
jq -r '.data.certificate' /tmp/win-cert.json > /tmp/cert.pem
jq -r '.data.private_key' /tmp/win-cert.json > /tmp/key.pem
jq -r '.data.issuing_ca' /tmp/win-cert.json > /tmp/ca.pem
openssl pkcs12 -export \
-in /tmp/cert.pem \
-inkey /tmp/key.pem \
-certfile /tmp/ca.pem \
-out /tmp/winpc-eaptls.p12 \
-name "EAP-TLS Certificate"
# Transfer to Windows, import with:
# certutil -importpfx -f winpc-eaptls.p12
Infrastructure Certificate Operations
# CHECK ALL INFRASTRUCTURE CERTS
for host in vault-01 ise-01 grafana prometheus; do
echo "=== $host ==="
echo | openssl s_client -connect "${host}.inside.domusdigitalis.dev:443" 2>/dev/null | \
openssl x509 -noout -subject -dates 2>/dev/null || echo " Connection failed"
done
# CERTIFICATE EXPIRY MONITORING
check_expiry() {
local host=$1
local port=${2:-443}
local warn_days=${3:-30}
EXPIRY=$(echo | openssl s_client -connect "$host:$port" 2>/dev/null | \
openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "$host: UNREACHABLE"
return 1
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 0 ]; then
echo "$host: EXPIRED!"
elif [ $DAYS_LEFT -lt $warn_days ]; then
echo "$host: WARNING - $DAYS_LEFT days left"
else
echo "$host: OK - $DAYS_LEFT days left"
fi
}
check_expiry "vault-01.inside.domusdigitalis.dev" 8200
check_expiry "ise-01.inside.domusdigitalis.dev" 443
check_expiry "grafana.inside.domusdigitalis.dev" 443
# VERIFY EAP-TLS CERTIFICATE CHAIN
openssl verify \
-CAfile ~/.secrets/certs/d000/DOMUS-CA-CHAIN.pem \
/etc/ssl/certs/modestus-razer-eaptls.pem
# UPDATE SYSTEM CA TRUST
# Debian/Ubuntu
sudo cp DOMUS-CA-CHAIN.pem /usr/local/share/ca-certificates/domus-ca.crt
sudo update-ca-certificates
# RHEL/Rocky/Fedora
sudo cp DOMUS-CA-CHAIN.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
# Arch Linux
sudo cp DOMUS-CA-CHAIN.pem /etc/ca-certificates/trust-source/anchors/
sudo trust extract-compat
# CERTIFICATE RENEWAL WORKFLOW
# 1. Generate new cert from Vault
vault write -format=json pki_int/issue/domus-client \
common_name="hostname.inside.domusdigitalis.dev" \
ttl="8760h" > /tmp/new-cert.json
# 2. Verify new cert
jq -r '.data.certificate' /tmp/new-cert.json | \
openssl x509 -noout -subject -dates
# 3. Backup old cert
cp /etc/ssl/certs/hostname-eaptls.pem /etc/ssl/certs/hostname-eaptls.pem.bak
# 4. Install new cert
jq -r '.data.certificate' /tmp/new-cert.json > /etc/ssl/certs/hostname-eaptls.pem
jq -r '.data.private_key' /tmp/new-cert.json > /etc/ssl/private/hostname-eaptls.key
chmod 600 /etc/ssl/private/hostname-eaptls.key
# 5. Restart services
sudo systemctl restart wpa_supplicant
# Or for NetworkManager:
nmcli connection down "Wired-802.1X-EAP-TLS"
nmcli connection up "Wired-802.1X-EAP-TLS"
# 6. Verify authentication
netapi ise mnt sessions | grep hostname
Certificate Gotchas
# WRONG: Missing newline in echo for s_client
echo "Q" | openssl s_client -connect host:443
# May hang or behave unexpectedly
# CORRECT: Use empty echo
echo | openssl s_client -connect host:443
# WRONG: Trusting self-signed without verification
curl -k https://untrusted-host/
# -k skips ALL verification!
# CORRECT: Provide CA certificate
curl --cacert ca.pem https://host/
# WRONG: Certificate but no SAN
openssl req -x509 -subj "/CN=myhost"
# Modern browsers require SAN, not just CN
# CORRECT: Always include SAN
openssl req -x509 -subj "/CN=myhost" -addext "subjectAltName=DNS:myhost"
# WRONG: Chain in wrong order
cat root.pem intermediate.pem leaf.pem > chain.pem
# Should be: leaf -> intermediate -> root
# CORRECT: Leaf first
cat leaf.pem intermediate.pem root.pem > chain.pem
# WRONG: Assuming cert file contains key
# PEM can contain cert, key, or both!
# CORRECT: Check what's in file
grep -E "BEGIN (CERTIFICATE|PRIVATE KEY|RSA PRIVATE KEY)" file.pem
# WRONG: Key permissions too open
chmod 644 private-key.pem
# Anyone can read!
# CORRECT: Restrict key permissions
chmod 600 private-key.pem
chown root:root private-key.pem
# WRONG: Expired intermediate CA
# Leaf cert valid, but intermediate expired = chain invalid
# CORRECT: Check entire chain expiry
for cert in $(cat chain.pem | awk '/BEGIN/,/END/' RS= ORS='\n\n'); do
echo "$cert" | openssl x509 -noout -subject -enddate
done
# WRONG: Missing CA in verification
openssl verify cert.pem
# Uses system CAs, might miss custom CA
# CORRECT: Specify CA explicitly
openssl verify -CAfile my-ca.pem cert.pem
# WRONG: SSH cert with wrong principals
vault write ssh/sign/domus-client public_key=@key.pub
# No principals = can't authenticate
# CORRECT: Specify all needed principals
vault write ssh/sign/domus-client \
public_key=@key.pub \
valid_principals="user1,user2,admin"
# WRONG: PKCS#12 without chain
openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.p12
# Missing intermediate = validation fails on import
# CORRECT: Include chain
openssl pkcs12 -export -in cert.pem -inkey key.pem -certfile chain.pem -out cert.p12
Quick Reference
# VIEW CERTIFICATE
openssl x509 -in cert.pem -noout -text # Full details
openssl x509 -in cert.pem -noout -subject -dates # Key info
# REMOTE CERTIFICATE
echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 -noout -subject -dates
# VERIFY CHAIN
openssl verify -CAfile ca.pem cert.pem
openssl verify -CAfile root.pem -untrusted intermediate.pem cert.pem
# KEY/CERT MATCH
openssl x509 -in cert.pem -noout -modulus | sha256sum
openssl rsa -in key.pem -noout -modulus | sha256sum
# Should match!
# GENERATE SELF-SIGNED
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 -keyout key.pem -out cert.pem \
-subj "/CN=hostname" -addext "subjectAltName=DNS:hostname"
# VAULT PKI
vault write pki_int/issue/role common_name="host.domain" ttl="8760h"
# SSH CERTIFICATES
ssh-keygen -Lf cert.pub # View
vault write ssh/sign/role public_key=@key.pub valid_principals="user"
# FORMAT CONVERSION
openssl x509 -in cert.pem -outform DER -out cert.der # PEM to DER
openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.p12 # To PKCS#12
openssl pkcs12 -in cert.p12 -out all.pem -nodes # From PKCS#12
# EXPIRY CHECK
echo | openssl s_client -connect host:443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2