diff options
Diffstat (limited to 'cmd/x509info/main.go')
-rw-r--r-- | cmd/x509info/main.go | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/cmd/x509info/main.go b/cmd/x509info/main.go new file mode 100644 index 0000000..c425c45 --- /dev/null +++ b/cmd/x509info/main.go @@ -0,0 +1,150 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "html/template" + "os" + "time" + + "github.com/fcuny/world/internal/version" +) + +const usage = `Usage: + x509-info [DOMAIN] + x509-info (-f long) [DOMAIN] + +Options: + -f, --format Format the result. Valid values: short, long. Default: short + -i, --insecure Skip the TLS validation. Default: false + -p, --port Specify the port. Default: 443 + -v, --version Print version information + -h, --help Print this message +` + +func main() { + flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) } + + var ( + portFlag int + outputFormatFlag string + insecureFlag bool + versionFlag bool + ) + + flag.IntVar(&portFlag, "port", 443, "Port to check") + flag.IntVar(&portFlag, "p", 443, "Port to check") + flag.StringVar(&outputFormatFlag, "format", "short", "Format the output") + flag.StringVar(&outputFormatFlag, "f", "short", "Format the output") + flag.BoolVar(&insecureFlag, "insecure", false, "Whether to bypass secure flag checks") + flag.BoolVar(&insecureFlag, "i", false, "Whether to bypass secure flag checks") + flag.BoolVar(&versionFlag, "version", false, "Print version information") + flag.BoolVar(&versionFlag, "v", false, "Print version information") + + flag.Parse() + + if versionFlag { + information := version.VersionAndBuildInfo() + fmt.Println(information) + return + } + + if flag.NArg() != 1 { + fmt.Fprintf(os.Stderr, "too many arguments: got %d, expected 1\n", flag.NArg()) + flag.Usage() + os.Exit(1) + } + + domain := flag.Arg(0) + + certs, err := getCertificates(domain, portFlag, insecureFlag) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + switch outputFormatFlag { + case "long": + printLong(certs) + default: + printShort(certs) + } +} + +func getCertificates(domain string, port int, insecureSkipVerify bool) ([]*x509.Certificate, error) { + conf := &tls.Config{ + InsecureSkipVerify: insecureSkipVerify, + } + + remote := fmt.Sprintf("%s:%d", domain, port) + + conn, err := tls.Dial("tcp", remote, conf) + if err != nil { + return nil, fmt.Errorf("failed to get the certificate for %s: %v", remote, err) + } + + defer conn.Close() + + certs := conn.ConnectionState().PeerCertificates + return certs, nil +} + +func printShort(certs []*x509.Certificate) { + cert := certs[0] + + now := time.Now() + remainingDays := cert.NotAfter.Sub(now) + + if remainingDays > 0 { + fmt.Printf("%s, valid until %s (%d days left)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Hours()/24)) + } else { + fmt.Printf("%s, not valid since %s (expired %d days ago)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Abs().Hours()/24)) + } +} + +const tmplLong = `certificate + version: {{ .Version }} + serial: {{ .SerialNumber }} + subject: {{ .Subject.CommonName }} + issuer: {{ .Issuer.CommonName }} + +validity: + not before: {{ rfc1123 .NotBefore }} + not after: {{ rfc1123 .NotAfter }} + validity days: {{ validFor .NotBefore .NotAfter }} + remaining days: {{ remainingDays .NotAfter }} + +SANs: +{{- range $i, $name := .DNSNames }} + • {{ $name }} +{{- end }} +` + +func printLong(certs []*x509.Certificate) { + funcMap := template.FuncMap{ + "validFor": func(before, after time.Time) int { + validForDays := after.Sub(before) + return int(validForDays.Hours() / 24) + }, + "remainingDays": func(notAfter time.Time) int { + now := time.Now() + remainingDays := notAfter.Sub(now) + return int(remainingDays.Hours() / 24) + }, + "rfc1123": func(date time.Time) string { + return date.Format(time.RFC1123) + }, + } + + tmpl, err := template.New("tmpl").Funcs(funcMap).Parse(tmplLong) + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, certs[0]) + if err != nil { + panic(err) + } +} |