diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-10-10 22:30:10 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-10-10 22:30:10 -0400 |
commit | e70b8fb7228f0b90c8c9a095af9b6a82c762eedf (patch) | |
tree | ad82488c3bfd641474fac8318f0b3eef33a282a1 /pem2html.go |
initial commit
Diffstat (limited to 'pem2html.go')
-rw-r--r-- | pem2html.go | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/pem2html.go b/pem2html.go new file mode 100644 index 0000000..be0405d --- /dev/null +++ b/pem2html.go @@ -0,0 +1,142 @@ +package main + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "time" + "html/template" + "sort" +) + +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...) + } +} + +var tmpl = template.Must(template.New("pem2html").Parse(`<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>CT log</title> + <style> + html { + height: 100%; + } + body { + font-size: 10px; + font-family: monospace; + height: 100%; + display: flex; + align-items: center; + } + body > * { + margin: auto; + } + table { + border-collapse: collapse; + } + td, th { + padding: 0.1em 0.25em; + border: solid 1px black; + white-space: nowrap; + } + td { + background-color: #F3F3F3; + } + td a { + display: block; + width: 100%; + height: 100%; + color: black; + } + </style> +</head> +<body> +<table> + <tr> + <th>Logged</th> + <th>NotBefore</th> + <th>NotAfter</th> + <th>Subject.CN</th> + <th>Issuer.O</th> + </tr> +{{range $cert := .}} + <tr> + <td><a href="{{$cert.Url}}">{{$cert.Updated.Local.Format "2006-01-02 15:04:05"}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.NotBefore.Local.Format "2006-01-02"}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.NotAfter.Local.Format "2006-01-02"}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.Subject.CommonName}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.Issuer.Organization}}</a></td> + </tr> +{{end}} +</table> +</body> +</html>`)) + +type Cert struct { + Url string + Updated time.Time + X509 *x509.Certificate +} + +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].Updated.UTC().After(l[j].Updated.UTC()) +} + +// 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.Url, ok = certPem.Headers["X-Crt-Sh-Url"] + handleBool(ok, "Did not get X-Crt-Sh-Url\n") + + str, ok := certPem.Headers["X-Crt-Sh-Updated"] + handleBool(ok, "Did not get X-Crt-Sh-Updated\n") + cert.Updated, err = time.Parse("2006-01-02T15:04:05Z", str) + handleErr(err, "Could not parse updated time") + + cert.X509, err = x509.ParseCertificate(certPem.Bytes) + handleErr(err, "Error parsing cert: %v\n") + + certs = append(certs, cert) + } + + sort.Sort(certs) + tmpl.Execute(os.Stdout, certs) +} |