-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
141 lines (123 loc) · 3.47 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Grabs certificates from a remote source
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"flag"
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"time"
)
var version = "X.X.X" // populated by build script
// holds the args supplied to the program
type commandArgs struct {
Timeout int
IncludeChain bool
UseDER bool
OutFile string
}
// Display usage then exit
func usage() {
usage := `Usage: ` + os.Args[0] + ` [Options] host:port`
usage += `
Grabs x509 certificate(s) from a remote host, format will default to PEM.
v` + version + ` Mike Nicholls 2022
Options:
-h, --help show this help message and exit
-c, --chain include the chain
-d, --der write in DER format instead of PEM
-w, --wait wait timeout for connection in seconds
-o, --out output to file instead of stdout
`
fmt.Fprint(flag.CommandLine.Output(), usage)
os.Exit(1)
}
// Helper func to handle errors that should exit the program
func handlErrFatal(err error) {
if err != nil {
log.Fatal("[!] ", err)
}
}
// Check that the args passed are valid, returns either the host string or shows usage
func checkArgs() string {
if len(flag.Args()) < 1 {
usage()
}
netAddr := flag.Arg(0)
_, strPort, ok := strings.Cut(netAddr, ":")
if !ok {
usage()
}
port, err := strconv.Atoi(strPort)
if err != nil {
usage()
}
if port < 1 {
usage()
}
return netAddr
}
// Makes a connection to the remote host and returns the certs and closes the connection
func getCerts(host string, args commandArgs) []*x509.Certificate {
log.Printf("[*] retrieving cert(s) from %s", host)
// not bothered about verification of cert
cfg := &tls.Config{InsecureSkipVerify: true}
dialer := &net.Dialer{Timeout: time.Duration(args.Timeout) * time.Second}
conn, err := tls.DialWithDialer(dialer, "tcp", host, cfg)
handlErrFatal(err)
defer conn.Close()
return conn.ConnectionState().PeerCertificates
}
// Writes the out bytes into a file, default is stdout
func writeOutput(out []byte, args commandArgs) {
fileOut := os.Stdout
if len(args.OutFile) > 0 {
f, err := os.OpenFile(args.OutFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
handlErrFatal(err)
defer f.Close()
fileOut = f
}
fileOut.Write(out)
}
// Processes the certs passed
func processCerts(certs []*x509.Certificate, args commandArgs) {
// only use the first item in the slice
if !args.IncludeChain {
certs = []*x509.Certificate{certs[0]}
}
log.Printf("[+] retrieved %d cert(s)", len(certs))
for _, cert := range certs {
if args.UseDER {
// Write the Raw DER
writeOutput(cert.Raw, args)
continue
}
// convert to PEM then write
pemBlock := &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
writeOutput(pem.EncodeToMemory(pemBlock), args)
}
}
func main() {
args := commandArgs{}
log.SetFlags(0)
log.SetPrefix("")
// setup and process args
flag.IntVar(&args.Timeout, "w", 20, "wait timeout")
flag.IntVar(&args.Timeout, "wait", 20, "wait timeout")
flag.BoolVar(&args.IncludeChain, "c", false, "include cetificate chain")
flag.BoolVar(&args.IncludeChain, "chain", false, "include cetificate chain")
flag.BoolVar(&args.UseDER, "d", false, "use DER format")
flag.BoolVar(&args.UseDER, "der", false, "use DER format")
flag.StringVar(&args.OutFile, "o", "", "output file to write to otherwise uses stdout")
flag.StringVar(&args.OutFile, "out", "", "output file to write to otherwise uses stdout")
flag.Usage = usage
flag.Parse()
host := checkArgs()
certs := getCerts(host, args)
processCerts(certs, args)
}