Created
August 3, 2026 22:38
-
-
Save conduition/6019884588e1b090f968a6c46f9e1ba0 to your computer and use it in GitHub Desktop.
Test a TLS server by dumping basic handshake metadata: version number, key-exchange/cipher algos, certificate chains, ECH use, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "crypto/tls" | |
| "crypto/x509" | |
| "encoding/pem" | |
| "flag" | |
| "fmt" | |
| "os" | |
| "strings" | |
| ) | |
| func init() { | |
| flag.Usage = func() { | |
| out := flag.CommandLine.Output() | |
| fmt.Fprintf(out, "Usage:\n") | |
| fmt.Fprintf(out, " %s [-ca <self_signed_cert.pem>] <host:port>\n", os.Args[0]) | |
| fmt.Fprintf(out, "\nOptions:\n") | |
| flag.PrintDefaults() | |
| } | |
| } | |
| func main() { | |
| var rootCertFile string | |
| flag.StringVar(&rootCertFile, "ca", "", "A custom root certificate authority to trust (PEM file path).") | |
| flag.Parse() | |
| args := flag.Args() | |
| if len(args) != 1 { | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| tlsConfig := &tls.Config{} | |
| if rootCertFile != "" { | |
| fileBytes, err := os.ReadFile(rootCertFile) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "failed to read root cert: %s\n", err) | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| block, _ := pem.Decode(fileBytes) | |
| if block == nil { | |
| fmt.Fprintf(os.Stderr, "failed to decode root cert as PEM\n") | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| rootCert, err := x509.ParseCertificate(block.Bytes) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "failed to parse root cert from decoded PEM: %s\n", err) | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| certPool := x509.NewCertPool() | |
| certPool.AddCert(rootCert) | |
| tlsConfig.RootCAs = certPool | |
| } | |
| conn, err := tls.Dial("tcp", args[0], tlsConfig) | |
| // Try again but don't verify the certs. | |
| if _, ok := err.(*tls.CertificateVerificationError); ok { | |
| conn, err = tls.Dial("tcp", args[0], &tls.Config{ | |
| InsecureSkipVerify: true, | |
| }) | |
| } | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "failed to dial: %s\n", err) | |
| os.Exit(1) | |
| } | |
| defer conn.Close() | |
| // inspect the handshake state | |
| state := conn.ConnectionState() | |
| fmt.Printf("TLS Version: 1.%d\n", state.Version-tls.VersionTLS10) | |
| fmt.Printf("Cipher Suite: %s\n", tls.CipherSuiteName(state.CipherSuite)) | |
| fmt.Printf("CurveID (NamedGroup): %s\n", state.CurveID) | |
| fmt.Printf("Peer certs: %d\n", len(state.PeerCertificates)) | |
| fmt.Printf("Server name: %s\n", state.ServerName) | |
| fmt.Printf("Server: %s\n", conn.RemoteAddr()) | |
| fmt.Printf("Encrypted Client Hello: %v\n", state.ECHAccepted) | |
| fmt.Println() | |
| if len(state.VerifiedChains) > 0 { | |
| fmt.Println("Verified certificate chains:") | |
| for _, chain := range state.VerifiedChains { | |
| for i, _ := range chain { | |
| cert := chain[len(chain)-i-1] | |
| fmt.Printf("[issuer=%q subject=%q algo=%s]", cert.Issuer.CommonName, cert.Subject.CommonName, cert.PublicKeyAlgorithm) | |
| if i < len(chain)-1 { | |
| fmt.Printf("\n%s-> ", strings.Repeat(" ", 2+i)) | |
| } | |
| } | |
| fmt.Println() | |
| } | |
| } else { | |
| fmt.Printf("*** certificate was not verified ***\n\n") | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment