summaryrefslogtreecommitdiff
path: root/bin-src/tls-pem2html.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2018-03-14 18:18:31 -0400
committerLuke Shumaker <lukeshu@lukeshu.com>2018-03-17 13:49:41 -0400
commitb54a1c9686eec3c1114e9b58cb67679ba59c45bd (patch)
tree0bdb2f3ed51ff077a8c3e337e4bc556aacec108e /bin-src/tls-pem2html.go
parent54feeb027d6e5a760b49769dfe695ea2591dc6fe (diff)
directories
Diffstat (limited to 'bin-src/tls-pem2html.go')
-rw-r--r--bin-src/tls-pem2html.go160
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")
+}