Add certexpiry.

This commit is contained in:
Kyle Isom 2017-01-05 21:02:28 -08:00
parent 858c9cb4e8
commit 9b18cc18d3
3 changed files with 130 additions and 3 deletions

View File

@ -11,6 +11,9 @@ Contents:
certchain/ Display the certificate chain from a
TLS connection.
certdump/ Dump certificate information.
certexpiry/ Print a list of certificate subjects and expiry times
or warn about certificates expiring within a certain
window.
certverify/ Verify a TLS X.509 certificate, optionally printing
the time to expiry and checking for revocations.
clustersh/ Run commands or transfer files across multiple
@ -20,16 +23,16 @@ Contents:
jlp/ JSON linter/prettifier.
pem2bin/ Dump the binary body of a PEM-encoded block.
pembody/ Print the body of a PEM certificate.
pemit/ Dump data to a PEM file.
pemit/ Dump data to a PEM file.
showimp/ List the external (e.g. non-stdlib and outside the
current working directory) imports for a Go file.
readchain/ Print the common name for the certificates
in a bundle.
showimp Display the external imports in a package.
showimp Display the external imports in a package.
stealchain/ Dump the verified chain from a TLS
connection.
tlskeypair/ Check whether a TLS certificate and key file match.
utc/ Convert times to UTC.
utc/ Convert times to UTC.
die/ Death of a program.
fileutil/ Common file functions.
lib/ Commonly-useful functions for writing Go programs.

24
cmd/certexpiry/README Normal file
View File

@ -0,0 +1,24 @@
certexpiry
Print a list of certificates and their expiry, or only warn about
upcoming expiries.
It takes a list of PEM-encoded certificates, and compares the NotAfter
value to the window given by the -t flag (which defaults to 2160 hours,
or 90 days). Alternatively, given the -q flag, it will only warn about
certificates expiring in the window.
Example, run on the cfssl-trust[1] CA bundle:
$ certexpiry -q ca-bundle.crt
/GPKIRootCA/C=KR/O=Government of Korea/OU=GPKI/SN=93008982654396041992798201139454296355 expires on 2017-03-15 06:00:04 +0000 UTC (in 1633h0m44.686144136s)
/CA DATEV BT 01/C=DE/O=DATEV eG/SN=139288328771231810070797444106717912243 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685385355s)
/CA DATEV STD 01/C=DE/O=DATEV eG/SN=142389455970744957119921172249094394891 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685236793s)
/CA DATEV INT 01/C=DE/O=DATEV eG/SN=169035066479776292612803392462688126470 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685208087s)
$ certexpiry ca-bundle.crt | head -5
/http://www.valicert.com//O=ValiCert, Inc./OU=ValiCert Class 3 Policy Validation Authority/L=ValiCert Validation Network/SN=1 expires on 2019-06-26 00:22:33 +0000 UTC (in 21619h22m44.060898709s)
/QuoVadis Root CA 2/C=BM/O=QuoVadis Limited/SN=1289 expires on 2031-11-24 18:23:33 +0000 UTC (in 130453h23m44.060878817s)
/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority/SN=149843929435818692848040365716851702463 expires on 2028-08-01 23:59:59 +0000 UTC (in 101419h0m10.06087362s)
/Equifax Secure Global eBusiness CA-1/C=US/O=Equifax Secure Inc./SN=1 expires on 2020-06-21 04:00:00 +0000 UTC (in 30287h0m11.060869101s)
/thawte Primary Root CA/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/SN=69529181992039203566298953787712940909 expires on 2036-07-16 23:59:59 +0000 UTC (in 171163h0m10.060864304s)

100
cmd/certexpiry/main.go Normal file
View File

@ -0,0 +1,100 @@
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/cloudflare/cfssl/helpers"
"github.com/kisom/goutils/die"
"github.com/kisom/goutils/lib"
)
var warnOnly bool
var leeway = 2160 * time.Hour // three months
func displayName(name pkix.Name) string {
var ns []string
if name.CommonName != "" {
ns = append(ns, name.CommonName)
}
for i := range name.Country {
ns = append(ns, fmt.Sprintf("C=%s", name.Country[i]))
}
for i := range name.Organization {
ns = append(ns, fmt.Sprintf("O=%s", name.Organization[i]))
}
for i := range name.OrganizationalUnit {
ns = append(ns, fmt.Sprintf("OU=%s", name.OrganizationalUnit[i]))
}
for i := range name.Locality {
ns = append(ns, fmt.Sprintf("L=%s", name.Locality[i]))
}
for i := range name.Province {
ns = append(ns, fmt.Sprintf("ST=%s", name.Province[i]))
}
if len(ns) > 0 {
return "/" + strings.Join(ns, "/")
}
die.With("no subject information in root")
return ""
}
func expires(cert *x509.Certificate) time.Duration {
return cert.NotAfter.Sub(time.Now())
}
func inDanger(cert *x509.Certificate) bool {
return expires(cert) < leeway
}
func checkCert(cert *x509.Certificate) {
warn := inDanger(cert)
name := displayName(cert.Subject)
name = fmt.Sprintf("%s/SN=%s", name, cert.SerialNumber)
expiry := expires(cert)
if warnOnly {
if warn {
fmt.Fprintf(os.Stderr, "%s expires on %s (in %s)\n", name, cert.NotAfter, expiry)
}
} else {
fmt.Printf("%s expires on %s (in %s)\n", name, cert.NotAfter, expiry)
}
}
func main() {
flag.BoolVar(&warnOnly, "q", false, "only warn about expiring certs")
flag.DurationVar(&leeway, "t", leeway, "warn if certificates are closer than this to expiring")
flag.Parse()
for _, file := range flag.Args() {
in, err := ioutil.ReadFile(file)
if err != nil {
lib.Warn(err, "failed to read file")
continue
}
certs, err := helpers.ParseCertificatesPEM(in)
if err != nil {
lib.Warn(err, "while parsing certificates")
continue
}
for _, cert := range certs {
checkCert(cert)
}
}
}