AgentSkillsCN

Skill

技能

SKILL.md
--- frontmatter
skill: ssl-certificate-checker
version: 1.0.0
author: ian@esoup.net
github: mcyork
author_fingerprint: 7097CE1EF54E0808FD3855427ED9682FF64286D0
signed: true
attestations: []
manifest_hash: sha256:db9259845e9734e5a520ea2fcf60525f06ee057c2e4301717353b0f4747f1c29

SSL Certificate Checker

A skill that teaches an LLM agent how to inspect and evaluate SSL/TLS certificates on remote hosts using openssl s_client.

Purpose

Check the SSL/TLS certificate of any remote host and produce a clear, actionable report covering: certificate validity, expiry, issuer, subject, Subject Alternative Names (SANs), and chain of trust status.

Prerequisites

  • openssl must be available on the system PATH (ships with macOS and most Linux distributions)
  • Network access to the target host on the specified port (default: 443)

Usage

When the user asks you to check an SSL certificate, follow the steps below. The user may provide:

  • A hostname (e.g., example.com)
  • A hostname with port (e.g., example.com:8443)
  • A URL (e.g., https://example.com/some/path) -- extract just the hostname

If a URL is provided, extract the hostname. If no port is specified, default to 443.


Step 1: Fetch the Certificate

Run the following command to connect to the host and retrieve the full certificate and chain:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST 2>/dev/null

Important flags:

  • -servername HOST enables SNI (Server Name Indication), which is required for hosts that serve multiple certificates on the same IP address. Without this, you may get the wrong certificate or a connection failure.
  • echo | sends an empty input so that openssl exits cleanly after the handshake instead of waiting for interactive input.
  • 2>/dev/null suppresses stderr diagnostic output (connection info, verification codes) so you get clean certificate data on stdout.

Capture the full output into a variable or temp file. You will parse multiple sections from it.


Step 2: Extract and Decode the Certificate

From the output of Step 1, extract the PEM-encoded certificate (the block between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----, inclusive) and decode it:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST 2>/dev/null | \
  openssl x509 -noout -text -dates -issuer -subject -ext subjectAltName

This single pipeline fetches the certificate and decodes it in one pass. The flags:

  • -noout suppresses printing the raw PEM block
  • -text prints the full human-readable certificate details
  • -dates prints notBefore and notAfter on their own lines (easy to parse)
  • -issuer prints the issuer distinguished name
  • -subject prints the subject distinguished name
  • -ext subjectAltName prints the SAN extension entries

Step 3: Check Days Until Expiry

To get a machine-readable check of whether the certificate expires within N days:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST 2>/dev/null | \
  openssl x509 -noout -checkend SECONDS

Where SECONDS is the number of seconds from now. Common values:

  • 0 = is it expired right now?
  • 2592000 = expires within 30 days?
  • 604800 = expires within 7 days?

The exit code tells you the result:

  • Exit code 0: certificate will NOT expire within that period (good)
  • Exit code 1: certificate WILL expire within that period (warning/critical)

To calculate exact days remaining, extract notAfter and compute:

bash
EXPIRY_DATE=$(echo | openssl s_client -connect HOST:PORT -servername HOST 2>/dev/null | \
  openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -j -f "%b %d %H:%M:%S %Y %Z" "$EXPIRY_DATE" "+%s" 2>/dev/null || \
  date -d "$EXPIRY_DATE" "+%s" 2>/dev/null)
NOW_EPOCH=$(date "+%s")
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "$DAYS_LEFT days until expiry"

Note: The date command syntax differs between macOS (date -j -f) and GNU/Linux (date -d). The command above tries macOS first, then falls back to Linux.


Step 4: Verify the Chain of Trust

To check whether the certificate chain is valid and trusted by the system's CA bundle:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST 2>&1 | head -20

Look for the Verify return code: line in the output. Key values:

  • Verify return code: 0 (ok) -- chain is valid and trusted
  • Verify return code: 10 (certificate has expired) -- certificate or a chain certificate has expired
  • Verify return code: 18 (self-signed certificate) -- the leaf certificate is self-signed
  • Verify return code: 19 (self-signed certificate in certificate chain) -- an intermediate or root in the chain is self-signed and not in the trust store
  • Verify return code: 20 (unable to get local issuer certificate) -- the chain is incomplete; a required intermediate or root CA is missing
  • Verify return code: 21 (unable to verify the first certificate) -- similar to 20; the issuer of the leaf certificate is not available

Also note the depth=N lines in the certificate chain section of the output. Each line represents one certificate in the chain from leaf (depth 0) to root.


Step 5: Produce the Report

Compile findings into a structured report with these sections:

Report Format

code
SSL Certificate Report: HOST:PORT
====================================

Subject:       CN=example.com, O=Example Inc, ...
Issuer:        CN=R11, O=Let's Encrypt, C=US
Serial:        0A:1B:2C:...

Valid From:    Jan  1 00:00:00 2026 GMT
Valid Until:   Apr  1 00:00:00 2026 GMT
Days Left:     46

SANs:          example.com, www.example.com, api.example.com

Chain Status:  Valid (Verify return code: 0 (ok))
Chain Depth:   3 (leaf -> intermediate -> root)

Overall:       PASS | WARN | FAIL

Decision Logic

  • PASS: Chain is valid (code 0), certificate is not expired, more than 30 days until expiry
  • WARN: Chain is valid but certificate expires within 30 days, OR minor issues detected (e.g., weak signature algorithm)
  • FAIL: Certificate is expired, chain verification failed, self-signed (unless the user is explicitly expecting self-signed), connection refused, or hostname mismatch

Handling Edge Cases

Connection Failure

If openssl s_client returns an error or cannot connect:

code
SSL Certificate Report: HOST:PORT
====================================
Status:  FAIL
Error:   Connection failed -- unable to reach HOST on port PORT.
         Possible causes: host is down, port is blocked by firewall,
         or the service does not support TLS on this port.

Verify the host is reachable before blaming the certificate:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST 2>&1 | head -5

If you see connect: Connection refused or connect: Operation timed out, the issue is network/service level, not certificate level.

Self-Signed Certificates

Self-signed certificates will show Verify return code: 18 or 19. Report as FAIL by default, but note:

code
Note: This is a self-signed certificate. This is expected for
development/internal environments but should not be used in production.
The certificate's integrity can still be evaluated -- only chain trust is absent.

Expired Certificates

If notAfter is in the past or checkend 0 returns exit code 1:

code
Overall:  FAIL
Reason:   Certificate expired on [DATE] ([N] days ago)
Action:   Certificate must be renewed immediately.

Hostname Mismatch

If the hostname the user asked about does not appear in the Subject CN or SANs list, flag it:

code
WARNING: Hostname "HOST" does not appear in the certificate's
Subject CN or Subject Alternative Names.
This will cause browser and client TLS errors.

STARTTLS Services

For services that use STARTTLS (SMTP, IMAP, FTP, etc.) rather than direct TLS, use the -starttls flag:

bash
echo | openssl s_client -connect HOST:PORT -servername HOST -starttls smtp 2>/dev/null

Supported protocols: smtp, pop3, imap, ftp, xmpp, lmtp, nntp, sieve, ldap.

Common ports and their STARTTLS protocols:

  • Port 25 or 587: -starttls smtp
  • Port 143: -starttls imap
  • Port 110: -starttls pop3
  • Port 21: -starttls ftp

Checking Multiple Hosts

If the user provides a list of hosts, check each one sequentially and produce a summary table at the end:

code
Host                    Status   Days Left   Issuer
---------------------   ------   ---------   ----------------------
example.com:443         PASS     46          Let's Encrypt
api.example.com:443     WARN     12          Let's Encrypt
old.example.com:443     FAIL     expired     DigiCert
internal.dev:8443       FAIL     self-signed (none)

Security Notes

  • This skill only performs read-only inspection of remote certificates. It does not modify any system state, install certificates, or change trust stores.
  • The openssl s_client command establishes a TLS connection to the target host. This is normal TLS handshake traffic and is not intrusive.
  • Do not use this skill to scan hosts you are not authorized to inspect. Certificate checking is generally benign, but respect the user's scope of authorization.
  • If a certificate check reveals security issues (expired cert, weak key, etc.), recommend remediation but do not take automated action on remote systems.