diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2018-03-14 18:18:31 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2018-03-17 13:49:41 -0400 |
commit | b54a1c9686eec3c1114e9b58cb67679ba59c45bd (patch) | |
tree | 0bdb2f3ed51ff077a8c3e337e4bc556aacec108e /bin-src/tls-pem2html.go | |
parent | 54feeb027d6e5a760b49769dfe695ea2591dc6fe (diff) |
directories
Diffstat (limited to 'bin-src/tls-pem2html.go')
-rw-r--r-- | bin-src/tls-pem2html.go | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/bin-src/tls-pem2html.go b/bin-src/tls-pem2html.go new file mode 100644 index 0000000..bc14f9a --- /dev/null +++ b/bin-src/tls-pem2html.go @@ -0,0 +1,160 @@ +package main + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "html/template" + "io/ioutil" + "os" + "sort" + "time" + + "./util" +) + +func handleErr(err error, str string, a ...interface{}) { + a = append([]interface{}{err}, a...) + if err != nil { + fmt.Fprintf(os.Stderr, str, a...) + os.Exit(1) + } +} + +func handleBool(ok bool, str string, a ...interface{}) { + if !ok { + fmt.Fprintf(os.Stderr, str, a...) + os.Exit(1) + } +} + +var tmpl = template.Must(template.New("pem2html"). + Funcs(template.FuncMap{ + "red": red, + "green": green, + "date": util.Date2HTML, + "datetime": util.DateTime2HTML, + "colorDatetime": util.DateTime2ColorHTML, + "htmlcell": util.HTMLCellEscapeString, + }).Parse(`<table class=sortable> + <caption> + <p>Live Certs (Updated {{.now | colorDatetime}})</p> + </caption> + <tr> + <th>NotBefore</th> + <th>NotAfter</th> + <th>Subject.CN</th> + <th>Socket</th> + </tr> +{{range $cert := .certs}} + <tr class="{{$cert.Class}}"> + <td style="background-color: {{$cert.X509.NotBefore | green}}"><a href="{{$cert.Url}}" title="{{$cert.Error}}">{{$cert.X509.NotBefore | date}}</a></td> + <td style="background-color: {{$cert.X509.NotAfter | red }}"><a href="{{$cert.Url}}" title="{{$cert.Error}}">{{$cert.X509.NotAfter | date}}</a></td> + <td><a href="{{$cert.Url}}" title="{{$cert.Error}}">{{$cert.X509.Subject.CommonName | htmlcell}}</a></td> + <td><a href="{{$cert.Url}}" title="{{$cert.Error}}">{{$cert.Socket | htmlcell}}</a></td> + </tr> +{{end}} +</table> +`)) + +func getNow() time.Time { + stat, err := os.Stdin.Stat() + if err == nil { + return stat.ModTime() + } else { + return time.Now() + } +} + +var now = getNow() + +func green(t time.Time) string { + max := byte(0xF3) + // When did we get the cert? + // - 30 days ago => 0 green + // - just now => max green + greenness := util.MapRange( + util.TimeRange{now.AddDate(0, 0, -30), now}, + util.ByteRange{0, max}, + t) + return fmt.Sprintf("#%02X%02X%02X", max-greenness, max, max-greenness) +} + +func red(t time.Time) string { + max := byte(0xF3) + // When with the cert expire? + // - now => max red + // - 30 days from now => 0 red + redness := util.MapRange( + util.TimeRange{now, now.AddDate(0, 0, 30)}, + util.ByteRange{max, 0}, + t) + return fmt.Sprintf("#%02X%02X%02X", max, max-redness, max-redness) +} + +type Cert struct { + Socket string + Error string + X509 *x509.Certificate +} + +func (cert Cert) Url() string { + return fmt.Sprintf("https://crt.sh/?serial=%036x", cert.X509.SerialNumber) +} + +func (cert Cert) Class() string { + if cert.Error == "" { + return "" + } else { + return "invalid" + } +} + +type Certs []Cert + +// Len is the number of elements in the collection. +func (l Certs) Len() int { + return len(l) +} + +// Less reports whether the element with +// index i should sort before the element with index j. +func (l Certs) Less(i, j int) bool { + return l[i].X509.NotAfter.After(l[j].X509.NotAfter) +} + +// Swap swaps the elements with indexes i and j. +func (l Certs) Swap(i, j int) { + tmp := l[i] + l[i] = l[j] + l[j] = tmp +} + +func main() { + data, err := ioutil.ReadAll(os.Stdin) + handleErr(err, "Error reading stdin: %v\n") + + var certs Certs + for len(data) > 0 { + var certPem *pem.Block + certPem, data = pem.Decode(data) + + var ok bool + var cert Cert + + cert.Socket, ok = certPem.Headers["X-Socket"] + handleBool(ok, "Did not get X-Socket\n") + + cert.Error, ok = certPem.Headers["X-Error"] + + cert.X509, err = x509.ParseCertificate(certPem.Bytes) + if err != nil { + cert.X509 = new(x509.Certificate) + } + + certs = append(certs, cert) + } + + sort.Sort(certs) + handleErr(tmpl.Execute(os.Stdout, map[string]interface{}{"certs": certs, "now": now}), "Could not execute template: %v\n") +} |