/*- * Copyright 2016 Square Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package main import ( "crypto/x509" "encoding/pem" "fmt" "os" "text/template" "time" "github.com/fatih/color" ) var layout = `{{if .Alias}}{{.Alias}} {{end}}Serial: {{.SerialNumber}} Not Before: {{.NotBefore | certStart}} Not After : {{.NotAfter | certEnd}} Signature : {{.SignatureAlgorithm | highlightAlgorithm}}{{if .IsSelfSigned}} (self-signed){{end}} Subject Info:{{if .Subject.Name.CommonName}} CommonName: {{.Subject.Name.CommonName}}{{end}}{{if .Subject.Name.Organization}} Organization: {{.Subject.Name.Organization}}{{end}}{{if .Subject.Name.OrganizationalUnit}} OrganizationalUnit: {{.Subject.Name.OrganizationalUnit}}{{end}}{{if .Subject.Name.Country}} Country: {{.Subject.Name.Country}}{{end}}{{if .Subject.Name.Locality}} Locality: {{.Subject.Name.Locality}}{{end}} Issuer Info:{{if .Issuer.Name.CommonName}} CommonName: {{.Issuer.Name.CommonName}}{{end}}{{if .Issuer.Name.Organization}} Organization: {{.Issuer.Name.Organization}}{{end}}{{if .Issuer.Name.OrganizationalUnit}} OrganizationalUnit: {{.Issuer.Name.OrganizationalUnit}}{{end}}{{if .Issuer.Name.Country}} Country: {{.Issuer.Name.Country}}{{end}}{{if .Issuer.Name.Locality}} Locality: {{.Issuer.Name.Locality}}{{end}}{{if .Subject.KeyID}} Subject Key ID : {{.Subject.KeyID | hexify}}{{end}}{{if .Issuer.KeyID}} Authority Key ID : {{.Issuer.KeyID | hexify}}{{end}}{{if .BasicConstraints}} Basic Constraints: CA:{{.BasicConstraints.IsCA}}{{if .BasicConstraints.MaxPathLen}}, pathlen:{{.BasicConstraints.MaxPathLen}}{{end}}{{end}}{{if .NameConstraints}} Name Constraints {{if .PermittedDNSDomains.Critical}}(critical){{end}}: {{range .NameConstraints.PermittedDNSDomains}} {{.}}{{end}}{{end}}{{if .KeyUsage}} Key Usage:{{range .KeyUsage | keyUsage}} {{.}}{{end}}{{end}}{{if .ExtKeyUsage}} Extended Key Usage:{{range .ExtKeyUsage}} {{. | extKeyUsage}}{{end}}{{end}}{{if .AltDNSNames}} Alternate DNS Names:{{range .AltDNSNames}} {{.}}{{end}}{{end}}{{if .AltIPAddresses}} Alternate IP Addresses:{{range .AltIPAddresses}} {{.}}{{end}}{{end}}{{if .EmailAddresses}} Email Addresses:{{range .EmailAddresses}} {{.}}{{end}}{{end}}{{if .Warnings}} Warnings:{{range .Warnings}} {{. | redify}}{{end}}{{end}}` type certWithName struct { name string file string cert *x509.Certificate } func createSimpleCertificateFromX509(block *pem.Block) simpleCertificate { raw, err := x509.ParseCertificate(block.Bytes) if err != nil { fmt.Fprintf(os.Stderr, "error reading cert: %s", err) os.Exit(1) } cert := certWithName{cert: raw} if val, ok := block.Headers[nameHeader]; ok { cert.name = val } if val, ok := block.Headers[fileHeader]; ok { cert.file = val } return createSimpleCertificate(cert) } // displayCert takes in a parsed certificate object // (for jceks certs, blank otherwise), and prints out relevant // information. Start and end dates are colored based on whether or not // the certificate is expired, not expired, or close to expiring. func displayCert(cert simpleCertificate) { funcMap := template.FuncMap{ "certStart": certStart, "certEnd": certEnd, "redify": redify, "highlightAlgorithm": highlightAlgorithm, "hexify": hexify, "keyUsage": keyUsage, "extKeyUsage": extKeyUsage, } t := template.New("Cert template").Funcs(funcMap) t, err := t.Parse(layout) if err != nil { // Should never happen panic(err) } err = t.Execute(os.Stdout, cert) if err != nil { // Should never happen panic(err) } } var ( green = color.New(color.Bold, color.FgGreen) yellow = color.New(color.Bold, color.FgYellow) red = color.New(color.Bold, color.FgRed) ) var algorithmColors = map[x509.SignatureAlgorithm]*color.Color{ x509.MD2WithRSA: red, x509.MD5WithRSA: red, x509.SHA1WithRSA: red, x509.SHA256WithRSA: green, x509.SHA384WithRSA: green, x509.SHA512WithRSA: green, x509.DSAWithSHA1: red, x509.DSAWithSHA256: red, x509.ECDSAWithSHA1: red, x509.ECDSAWithSHA256: green, x509.ECDSAWithSHA384: green, x509.ECDSAWithSHA512: green, } // highlightAlgorithm changes the color of the signing algorithm // based on a set color map, e.g. to make SHA-1 show up red. func highlightAlgorithm(sigAlg simpleSigAlg) string { sig := x509.SignatureAlgorithm(sigAlg) color, ok := algorithmColors[sig] if !ok { return algString(sig) } return color.SprintFunc()(algString(sig)) } // certStart takes a given start time for the validity of // a certificate and returns that time colored properly // based on how close it is to expiry. If it's more than // a day after the certificate became valid the string will // be green. If it has been less than a day the string will // be yellow. If the certificate is not yet valid, the string // will be red. func certStart(start time.Time) string { now := time.Now() day, _ := time.ParseDuration("24h") threshold := start.Add(day) if now.After(threshold) { return green.SprintfFunc()(start.String()) } else if now.After(start) { return yellow.SprintfFunc()(start.String()) } else { return red.SprintfFunc()(start.String()) } } // certEnd takes a given end time for the validity of // a certificate and returns that time colored properly // based on how close it is to expiry. If the certificate // is more than a month away from expiring it returns a // green string. If the certificate is less than a month // from expiry it returns a yellow string. If the certificate // is expired it returns a red string. func certEnd(end time.Time) string { now := time.Now() month, _ := time.ParseDuration("720h") threshold := now.Add(month) if threshold.Before(end) { return green.SprintfFunc()(end.String()) } else if now.Before(end) { return yellow.SprintfFunc()(end.String()) } else { return red.SprintfFunc()(end.String()) } } var badSignatureAlgorithms = []x509.SignatureAlgorithm{ x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.DSAWithSHA1, x509.ECDSAWithSHA1, } func redify(text string) string { return red.SprintfFunc()("%s", text) }