Back to Articles

genkiroid/cert: Inspect TLS Certificates Without the OpenSSL Ceremony

[ View on GitHub ]

genkiroid/cert: Inspect TLS Certificates Without the OpenSSL Ceremony

Hook

While everyone reaches for openssl s_client -connect to inspect certificates, they're launching a tool designed in 1998 that requires parsing human-readable text output. There's a simpler way built for modern automation.

Context

Certificate inspection is a routine DevOps task—checking expiration dates before renewals, verifying SANs match your domains, troubleshooting TLS handshake failures, or auditing certificate chains during security reviews. The default tool for this work has been OpenSSL's s_client command, which establishes a connection and dumps verbose output that you then grep, awk, or pipe to openssl x509 for parsing.

The problem isn't that OpenSSL doesn't work—it absolutely does. The problem is the ceremony around it. Checking a certificate expiration requires openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates. Want JSON output for your monitoring script? You're writing your own parser. Need to check 50 domains? You're building bash loops with timeout handling. The genkiroid/cert tool eliminates this friction by doing one thing exceptionally well: extracting certificate information in formats that integrate cleanly into modern workflows, whether you're typing commands interactively or building automation pipelines.

Technical Insight

Concurrent Processing

domains, ports, flags

tls.Dial with config

connection state

X.509 certificates

cert metadata

plain/markdown/JSON

tls.Config

CLI Input Parser

TLS Connection Manager

crypto/tls Handshake

Certificate Chain Extractor

Certificate Parser

Template Formatter

Formatted Output

Timeout & Verification Config

System architecture — auto-generated

Under the hood, cert is built on Go's crypto/tls package, which handles the heavy lifting of TLS handshakes and X.509 certificate parsing. The architecture is refreshingly straightforward: the tool accepts domain names, establishes TLS connections, extracts certificate data from the connection state, and formats the output using Go's template system. There's no complex state management or elaborate abstractions—just a focused pipeline from connection to output.

Here's what basic usage looks like compared to OpenSSL:

# OpenSSL: multi-step, text parsing required
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -text

# cert: single command, structured output
cert example.com

# JSON output for scripting
cert -format json example.com | jq '.[] | .notAfter'

The power becomes apparent when you need to inspect non-HTTP TLS services. Many services use TLS—IMAP on port 993, SMTP with STARTTLS on 587, PostgreSQL on 5432—but most certificate tools assume HTTPS. Cert treats ports as first-class configuration:

# Check mail server certificates
cert -port 993 imap.example.com
cert -port 587 smtp.example.com

# Multiple domains with custom timeout
cert -timeout 5 example.com cloudflare.com github.com

One of cert's most valuable features for troubleshooting is cipher suite selection, which lets you force specific cryptographic algorithms. This matters because modern servers often present different certificates based on the cipher suite negotiated. A server might serve an RSA certificate to clients requesting RSA cipher suites and an ECDSA certificate to clients requesting ECDSA ciphers—two completely different certificate chains on the same domain. OpenSSL can do this, but requires memorizing cipher suite syntax. Cert simplifies it:

# Force RSA to see the RSA certificate
cert -cipher TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 example.com

# Force ECDSA to see the ECDSA certificate
cert -cipher TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 example.com

The template-based output system is where cert truly shines for automation. The default format is human-readable, but you can switch to markdown for documentation or JSON for machine parsing:

# Generate a markdown table of certificate info
cert -format markdown example.com cloudflare.com

# Extract just expiration dates for monitoring
cert -format json example.com | jq -r '.[] | "\(.commonName): \(.notAfter)"'

# Find certificates expiring within 30 days
cert -format json $(cat domains.txt) | jq -r '.[] | select(.notAfter | fromdateiso8601 < (now + 2592000)) | .commonName'

The certificate chain inspection reveals another thoughtful design decision. Rather than just showing you the leaf certificate (the one presented by the server), cert exposes the entire chain including intermediate CAs. This is critical when debugging trust issues—often the problem isn't the leaf certificate but a missing or misconfigured intermediate. The tool parses each certificate in the chain and presents them in order, making it trivial to verify your chain is complete.

Concurrency support means you can check hundreds of domains quickly without manual parallelization. Pass multiple domains and cert checks them concurrently with proper timeout handling. This transforms what would be a slow serial bash loop into a fast parallel operation without writing any concurrent code yourself.

Gotcha

The tool has a quirky limitation around TLS 1.3: when you specify a cipher suite, cert artificially restricts the connection to TLS 1.2. This isn't a bug but a workaround for how Go's TLS 1.3 implementation works. In TLS 1.3, cipher suite selection happens differently—the client provides a list but can't force a specific cipher in the same way TLS 1.2 allowed. Since cert's cipher suite feature exists specifically to inspect different certificate types (RSA vs ECDSA), it drops back to TLS 1.2 where cipher forcing actually works. This means if a server only supports TLS 1.3, cipher suite selection won't work at all.

The validation options are also fairly binary. You can skip certificate verification entirely with -skip-verify, which is useful for inspecting expired or self-signed certificates, but there's no middle ground. You can't, for example, skip hostname validation while still checking expiration, or ignore untrusted roots while validating the chain structure. OpenSSL provides more granular control here. Additionally, cert doesn't support client certificate authentication (mutual TLS), so if you need to inspect a service that requires presenting a client certificate, you're back to OpenSSL or other tools.

Verdict

Use if: You regularly inspect certificates across multiple services (not just HTTPS), you're building automation that needs structured output formats, you want fast concurrent checking of many domains, or you're tired of memorizing OpenSSL incantations and just want certificate data without the ceremony. This tool is perfect for SRE teams building certificate expiration monitors, DevOps engineers troubleshooting TLS configurations, or anyone who checks certificates more than once a week. Skip if: You need detailed TLS handshake debugging beyond certificate inspection, you work with mutual TLS scenarios requiring client certificates, you need TLS 1.3 cipher suite inspection specifically, or you're already comfortable with OpenSSL and don't value the convenience of structured output. For those edge cases, stick with OpenSSL's verbose output or specialized tools like testssl.sh.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/genkiroid-cert.svg)](https://starlog.is/api/badge-click/developer-tools/genkiroid-cert)