127 lines
2.3 KiB
Go
127 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/jedib0t/go-pretty/v6/table"
|
|
"github.com/jedib0t/go-pretty/v6/text"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
type Config struct {
|
|
Hosts []string `yaml:"hosts"`
|
|
}
|
|
|
|
func readConfig(config string) Config {
|
|
c := Config{}
|
|
|
|
y, err := ioutil.ReadFile(config)
|
|
if err != nil {
|
|
fmt.Printf("fatal: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = yaml.Unmarshal(y, &c)
|
|
if err != nil {
|
|
fmt.Printf("fatal: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
return c
|
|
}
|
|
|
|
func getPeerCertificates(h string, port int, timeout int) ([]*x509.Certificate, error) {
|
|
conn, err := tls.DialWithDialer(
|
|
&net.Dialer{
|
|
Timeout: time.Duration(timeout) * time.Second,
|
|
},
|
|
protocol,
|
|
h+":"+strconv.Itoa(port),
|
|
&tls.Config{
|
|
ServerName: h,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := conn.Handshake(); err != nil {
|
|
return nil, err
|
|
}
|
|
return conn.ConnectionState().PeerCertificates, nil
|
|
}
|
|
|
|
func getCells(t table.Writer, host string, port, timeout int, wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
certs, err := getPeerCertificates(host, port, timeout)
|
|
if err != nil {
|
|
fmt.Printf("err: %s\n", err)
|
|
return // skip if target host invalid
|
|
}
|
|
|
|
for _, c := range certs {
|
|
if c.IsCA {
|
|
continue
|
|
}
|
|
t.AppendRows([]table.Row{{
|
|
host + ":" + strconv.Itoa(port),
|
|
(*c).Subject.CommonName,
|
|
strings.Join((*c).DNSNames, "\n"),
|
|
(*c).NotBefore,
|
|
(*c).NotAfter,
|
|
(*c).PublicKeyAlgorithm.String(),
|
|
(*c).Issuer.CommonName,
|
|
}})
|
|
}
|
|
}
|
|
|
|
func prettyPrintCertsInfo(config string, timeout int) {
|
|
rc := readConfig(config)
|
|
if len(rc.Hosts) <= 0 {
|
|
fmt.Printf("key not found, or empty input\n")
|
|
return
|
|
}
|
|
|
|
t := table.NewWriter()
|
|
t.SetOutputMirror(os.Stdout)
|
|
t.AppendHeader(table.Row{
|
|
"Host",
|
|
"Common Name",
|
|
"DNS Names",
|
|
"Not Before",
|
|
"Not After",
|
|
"PublicKeyAlgorithm",
|
|
"Issuer",
|
|
})
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for _, target := range rc.Hosts {
|
|
p := defaultPort
|
|
ts := strings.Split(target, ":")
|
|
if len(ts) == 2 {
|
|
tp, err := strconv.Atoi(ts[1])
|
|
if err != nil {
|
|
fmt.Errorf("err: invalid port [%s], assume target port is 443\n", target)
|
|
} else {
|
|
p = tp
|
|
}
|
|
}
|
|
wg.Add(1)
|
|
|
|
go getCells(t, ts[0], p, timeout, &wg)
|
|
}
|
|
wg.Wait()
|
|
|
|
t.Style().Format.Header = text.FormatDefault
|
|
t.Render()
|
|
}
|