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(` CT log {{range $cert := .}} {{end}}
Logged NotBefore NotAfter Subject.CN Issuer.O
{{$cert.Updated.Local.Format "2006-01-02 15:04:05"}} {{$cert.X509.NotBefore.Local.Format "2006-01-02"}} {{$cert.X509.NotAfter.Local.Format "2006-01-02"}} {{$cert.X509.Subject.CommonName}} {{$cert.X509.Issuer.Organization}}
`)) 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) }