1 min read #devops
On this page

Certificate Operations Manual

openssl

# View remote server certificate
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -text -noout
# -servername: Specify SNI (required when multiple certificates share the same IP)
# </dev/null: Immediately close the connection
# Output includes: Subject, Issuer, Validity, SAN, Public Key, Signature

# View local certificate file
openssl x509 -text -noout -in cert.pem
openssl x509 -text -noout -in cert.pem | grep -E 'Not Before|Not After|DNS:'

# Check if the certificate has expired
openssl x509 -checkend 86400 -noout -in cert.pem && echo "OK" || echo "EXPIRED"

# Generate private key (ECDSA P-256, modern recommendation)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
# Or RSA 2048 (for compatibility with older systems)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out key.pem

# Generate CSR (Certificate Signing Request, for CA issuance)
openssl req -new -key key.pem -out csr.pem -subj "/CN=example.com"
# CSR with SAN (required for modern browsers, Chrome ignores CN):
cat > san.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
DNS.2 = *.example.com
EOF
openssl req -new -key key.pem -out csr.pem -config san.cnf

# Self-signed certificate (for testing, not trusted)
openssl req -new -x509 -days 365 -key key.pem -out cert.pem -subj "/CN=localhost"

# Verify certificate chain
openssl verify -CAfile ca-bundle.crt cert.pem

# View certificate fingerprint (to match known certificates)
openssl x509 -fingerprint -sha256 -noout -in cert.pem

acme.sh

# DNS-01 issuance (Alibaba Cloud DNS) — supports wildcard *.example.com
export Ali_Key="xxx" Ali_Secret="yyy"
acme.sh --issue --dns dns_ali -d example.com -d '*.example.com'
# → Certificates are generated in the ~/.acme.sh/example.com_ecc/ directory

# Install certificate to target location + reload hook
acme.sh --install-cert -d example.com \
  --cert-file /etc/caddy/certs/example.crt \
  --key-file /etc/caddy/certs/example.key \
  --fullchain-file /etc/caddy/certs/example-fullchain.crt \
  --reloadcmd "systemctl reload caddy"

# Manual renewal
acme.sh --renew -d example.com --force

# Automatic renewal (cron)
acme.sh --cron
# → Checks all certificates, auto-renews 30 days before expiration, runs reloadcmd upon success

# View issued certificates
acme.sh --list

certbot

# DNS-01 issuance (Tencent Cloud DNSPod)
certbot certonly --dns-tencentcloud \
  --dns-tencentcloud-credentials /etc/letsencrypt/tencentcloud.ini \
  --dns-tencentcloud-propagation-seconds 30 \
  -d example.com

# Renewal (dry-run)
certbot renew --dry-run

# View issued certificates
certbot certificates

Troubleshooting

# Certificate expired
openssl x509 -text -noout -in cert.pem | grep "Not After"

# Incomplete certificate chain (curl reports SSL error)
openssl verify -CAfile ca-bundle.crt cert.pem
# → Missing intermediate CA → Need fullchain.pem (cert + intermediates)

# Let's Encrypt rate limit
# → Failed validation limit: 5 failures per account per hostname per hour
# → Test using --dry-run or staging environment: --server https://acme-staging-v02.api.letsencrypt.org/directory

# acme.sh automatic renewal did not run
# → Check crontab: crontab -l | grep acme.sh
# → Or manually: acme.sh --cron --force