Charlie KrugThe Build Log

← All posts

To read a broken cert chain, a client has to stop rejecting it

Porthole opens one TLS connection, accepts whatever chain the server presents so it can actually show you a broken one, then validates every hop itself and animates the tree as each link checks out.

A browser handed a broken certificate chain does exactly one useful thing: it refuses to connect and shows you a red interstitial. That's the correct behavior, and it's also useless in the one moment you care most, when the broken chain is the thing you're trying to inspect. Your options collapse to openssl s_client -connect host:443 -showcerts and a scrollback of PEM blocks, or a browser tab pointed at SSL Labs while a remote scanner takes its time.

Porthole is a terminal app for the five-second glance in between. Type a domain, press Enter, and it draws the certificate chain as a color-coded tree: leaf, each intermediate, then the root, every node appearing as its hop is validated. A panel beside it shows the negotiated TLS version and cipher suite, the leaf's expiry date, and whether the origin sends HSTS. It exists because none of the existing tools are built to be looked at fast, and I spend more time than I'd like squinting at openssl output on servers I manage.

Accept the chain, but don't get lied to

The interesting decision is buried in the TLS handshake. Porthole connects with a rustls verifier that accepts any presented chain, because rejecting broken and self-signed chains would defeat the entire point. But accepting the chain is not the same as trusting the peer. rustls still cryptographically checks the handshake signature, which the server makes with the leaf certificate's private key. So a host can hand Porthole a fraudulent chain to display, but it cannot present a leaf it does not actually hold the key for. The certificate list is just data the server ships; the real proof of possession is a signature over the handshake. Porthole separates those two things on purpose: show me whatever you've got, but you still have to prove you own the front of it.

The trust verdict then happens locally, in Porthole, not in the TLS library. It parses the presented DER certificates with x509-parser and walks the chain hop by hop. Each hop's signature is verified against the next certificate's public key, and its not_before/not_after window is checked against the clock. The terminal hop is resolved against a compiled-in webpki-roots store by public key, so a self-signed or unknown root is flagged untrusted instead of waved through. That's why every node carries a status glyph that reads before the color even registers: valid, failed (expired, not yet valid, or a broken signature), and for the honest middle case, an issuer whose dates check out but which chains to no trust anchor Porthole knows about.

The animation is doing real work

The build animation, nodes revealing one at a time from leaf to root, is the part that looks like decoration and isn't. Each node appears only after its hop has actually been validated: signature chained to the issuer, dates confirmed in range. The pace you see is the pace of the work, not a cosmetic timer counting down. When a chain stalls on a bad intermediate, it stalls in the same place the validation logic gave up, which turns out to be a surprisingly good way to see where a chain breaks rather than just that it did.

One honest limitation worth stating: that green Chain: VALID means the chain signed cleanly to a Mozilla-trusted root as of the webpki-roots snapshot baked into your binary at build time. It's a frozen copy of the trust list, reproducible and fully offline, but it can drift. A root added to Mozilla's program after you compiled won't be recognized until you rebuild. For a glance-tool that's the right trade, but it's a snapshot, not a live feed, and I'd rather say so than let you assume otherwise.

Try it

Install it with cargo install --git https://github.com/ctkrug/porthole, then run porthole google.com and watch the tree build to a green VALID. The more instructive run is a deliberately broken host: try porthole expired.badssl.com or self-signed.badssl.com and watch a node land on or with the reason spelled out inline, the exact output a browser would hide behind a warning page. Arrow-key to any node and press Enter for its full subject and issuer DN, serial, and key algorithm; press n for the next domain, q to quit. The whole thing, verifier and validation logic included, is on GitHub.

Porthole is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…