-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chkcerts.go
165 lines (143 loc) · 3.98 KB
/
chkcerts.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Usage
//
// go run chkcerts.go https://chrisshort.net
//
// go run chkcerts.go https://chrisshort.net 90
package main
import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"fmt"
"net/http"
"os"
"strconv"
"time"
"errors"
"github.com/fatih/color"
)
func main() {
if len(os.Args) < 2 || len(os.Args) > 3 {
fmt.Println("Please provide a URL (include https://) and an optional number of days to highlight expiring certificates")
os.Exit(1)
}
// Parse the URL and number of days
url := os.Args[1]
var days int = -1 // Default value if days argument is not provided
// Check if the number of days argument is provided
if len(os.Args) == 3 {
daysStr := os.Args[2]
var err error
days, err = parseDays(daysStr)
if err != nil {
fmt.Println("Invalid number of days:", err)
os.Exit(1)
}
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
// This is required to allow self-signed certificates to be checked
// It's my opinion that this is a bad idea, but it's the only way to accommodate some users
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: tr}
// Check if the URL is valid
resp, err := client.Get(url)
if err != nil {
fmt.Printf("Error connecting to %s: %s\n", url, err)
os.Exit(1)
}
defer resp.Body.Close()
// Check if the response was successful
certs := resp.TLS.PeerCertificates
var validChain bool = true
for i := 0; i < len(certs)-1; i++ {
if certs[i].Issuer.CommonName != certs[i+1].Subject.CommonName {
validChain = false
break
}
}
for _, cert := range certs {
fmt.Printf("Subject: %s\n", cert.Subject.CommonName)
fmt.Printf("Issuer: %s\n", cert.Issuer.CommonName)
fmt.Printf("Valid from: %s\n", cert.NotBefore)
fmt.Printf("Valid until: %s", cert.NotAfter)
// Check if the certificate is expired
if days != -1 {
daysLeft := int(time.Until(cert.NotAfter).Hours() / 24)
if daysLeft <= days {
color.Set(color.Bold, color.FgRed)
fmt.Printf(" (%d days left)\n", daysLeft)
color.Unset()
} else {
fmt.Println()
}
} else {
fmt.Println()
}
fmt.Printf("Serial number: %s\n", cert.SerialNumber.String())
fmt.Printf("DNS Names: %v\n", cert.DNSNames)
fmt.Printf("IP Addresses: %v\n", cert.IPAddresses)
fmt.Printf("Signature algorithm: %s\n", cert.SignatureAlgorithm.String())
// Obtain the cipher information
state := resp.TLS
if state != nil {
fmt.Printf("Cipher in use: %s\n", tls.CipherSuiteName(state.CipherSuite))
}
// Print KeyUsage information if available
if cert.KeyUsage != 0 {
fmt.Println("KeyUsage:")
printKeyUsage(cert.KeyUsage)
}
// Calculate and print the SHA-256 fingerprint
fingerprint := sha256.Sum256(cert.Raw)
fmt.Printf("Fingerprint (SHA-256): %s\n", hex.EncodeToString(fingerprint[:]))
// Check if the HSTS header is present
hstsHeader := resp.Header.Get("Strict-Transport-Security")
if hstsHeader != "" {
fmt.Println("HSTS Header:", hstsHeader)
}
fmt.Println("-----")
}
// Print the validity of the certificate chain
if validChain {
color.Set(color.Bold, color.FgGreen)
fmt.Println("Certificate chain is valid and in the correct order.")
} else {
color.Set(color.Bold, color.FgRed)
fmt.Println("Certificate chain is invalid or not in the correct order.")
}
}
// printKeyUsage prints the key usage flags of a certificate.
func printKeyUsage(keyUsage x509.KeyUsage) {
usageStrings := []string{
"Digital Signature",
"Content Commitment",
"Key Encipherment",
"Data Encipherment",
"Key Agreement",
"Certificate Signing",
"CRL Signing",
"Encipher Only",
"Decipher Only",
}
// Print the key usage flags of a certificate.
for i, usage := range usageStrings {
if keyUsage&(1<<i) != 0 {
fmt.Printf("- %s\n", usage)
}
}
}
// parseDays parses the number of days from a string.
func parseDays(daysStr string) (int, error) {
days, err := strconv.Atoi(daysStr)
if err != nil {
return 0, err
}
if days < 0 {
return 0, errors.New("number of days cannot be negative")
}
return days, nil
}