From e37596324344ff23c5303393fef1e0bb8712393d Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 14 Nov 2025 15:21:38 -0800 Subject: [PATCH] cmd: add tlsinfo. --- cmd/tlsinfo/README.txt | 34 ++++++++++++++++++++++ cmd/tlsinfo/main.go | 64 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 cmd/tlsinfo/README.txt create mode 100644 cmd/tlsinfo/main.go diff --git a/cmd/tlsinfo/README.txt b/cmd/tlsinfo/README.txt new file mode 100644 index 0000000..da06dde --- /dev/null +++ b/cmd/tlsinfo/README.txt @@ -0,0 +1,34 @@ +tlsinfo: show TLS version, cipher, and peer certificates +--------------------------------------------------------- + +Description + tlsinfo connects to a TLS server and prints the negotiated TLS version and + cipher suite, followed by details for each certificate in the server’s + presented chain (as provided by the server). + +Usage + tlsinfo + +Output + The program prints the negotiated protocol and cipher, then one section per + certificate in the order received from the server. Example fields: + TLS Version: TLS 1.3 + Cipher Suite: TLS_AES_128_GCM_SHA256 + Certificate 1 + Subject: CN=example.com, O=Example Corp, C=US + Issuer: CN=Example Root CA, O=Example Corp, C=US + DNS Names: [example.com www.example.com] + Not Before: 2025-01-01 00:00:00 +0000 UTC + Not After: 2026-01-01 23:59:59 +0000 UTC + +Examples + # Inspect a public HTTPS endpoint + tlsinfo example.com:443 + +Notes + - Verification is intentionally disabled (InsecureSkipVerify=true). The tool + does not validate the server certificate or hostname; it is for inspection + only. + - The SNI/ServerName is inferred from when applicable. + - You must specify a port (e.g., 443 for HTTPS). + - The entire certificate chain is printed exactly as presented by the server. diff --git a/cmd/tlsinfo/main.go b/cmd/tlsinfo/main.go new file mode 100644 index 0000000..0d2fb45 --- /dev/null +++ b/cmd/tlsinfo/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "os" +) + +func main() { + if len(os.Args) != 2 { + fmt.Printf("Usage: %s ‹hostname:port>\n", os.Args[0]) + os.Exit(1) + } + + hostPort := os.Args[1] + conn, err := tls.Dial("tcp", hostPort, &tls.Config{ + InsecureSkipVerify: true, + }) + + if err != nil { + fmt.Printf("Failed to connect to the TLS server: %v\n", err) + os.Exit(1) + } + defer conn.Close() + state := conn.ConnectionState() + printConnectionDetails(state) +} + +func printConnectionDetails(state tls.ConnectionState) { + version := tlsVersion(state.Version) + cipherSuite := tls.CipherSuiteName(state.CipherSuite) + fmt.Printf("TLS Version: %s\n", version) + fmt.Printf("Cipher Suite: %s\n", cipherSuite) + printPeerCertificates(state.PeerCertificates) +} + +func tlsVersion(version uint16) string { + switch version { + + case tls.VersionTLS13: + return "TLS 1.3" + case tls.VersionTLS12: + + return "TLS 1.2" + case tls.VersionTLS11: + return "TLS 1.1" + case tls.VersionTLS10: + return "TLS 1.0" + default: + return "Unknown" + } +} + +func printPeerCertificates(certificates []*x509.Certificate) { + for i, cert := range certificates { + fmt.Printf("Certificate %d\n", i+1) + fmt.Printf("\tSubject: %s\n", cert.Subject) + fmt.Printf("\tIssuer: %s\n", cert.Issuer) + fmt.Printf("\tDNS Names: %v\n", cert.DNSNames) + fmt.Printf("\tNot Before: %s\n:", cert.NotBefore) + fmt.Printf("\tNot After: %s\n", cert.NotAfter) + } +}