diff --git a/README.md b/README.md index 6e1c573..28ffcda 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ Contents: certchain/ Display the certificate chain from a TLS connection. certdump/ Dump certificate information. - certverify/ Verify a TLS X.509 certificate. + 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 servers via SSH. csrpubdump/ Dump the public key from an X.509 diff --git a/cmd/certverify/README b/cmd/certverify/README index 66cdc4b..be58221 100644 --- a/cmd/certverify/README +++ b/cmd/certverify/README @@ -6,31 +6,40 @@ It does not check for revocations (though this is a planned feature), and it does not check the hostname (it deals only in certificate files). [ Usage ] - certverify [-ca bundle] [-f] [-i bundle] [-v] certificate + certverify [-ca bundle] [-f] [-i bundle] [-r] [-v] certificate [ Flags ] - -ca bundle Specify the path to the CA certificate bundle - to use. - -f Force the use of the intermediate bundle, ignoring - any intermediates bundled with the certificate. - -i bundle Specify the path to the intermediate certificate - bundle to use. - -v Print extra information during the program's run. - If the certificate validates, also prints 'OK'. + -ca bundle Specify the path to the CA certificate bundle + to use. + -f Force the use of the intermediate bundle, ignoring + any intermediates bundled with the certificate. + -i bundle Specify the path to the intermediate certificate + bundle to use. + -r Print revocation and expiry information. + -v Print extra information during the program's run. + If the certificate validates, also prints 'OK'. [ Examples ] To verify the 'www.pem' certificate against the system roots: - $ certverify www.pem - $ echo $? - 0 + $ certverify www.pem + $ echo $? + 0 To verify the 'www.pem' certificate against the 'ca-cert.pem' CA certificate bundle, and seeing a mismatch: - $ certverify -ca ca-cert.pem www.pem - Verification failed: x509: certificate signed by unknown authority - $ echo $? - 1 + $ certverify -ca ca-cert.pem www.pem + Verification failed: x509: certificate signed by unknown authority + $ echo $? + 1 + +Using the stealchain (../stealchain) util, print revocation and expiry +information for google.com: + + $ stealchain google.com + [+] wrote google.com.pem. + $ certverify -r google.com.pem + certificate expires in 53d. diff --git a/cmd/certverify/main.go b/cmd/certverify/main.go index c1d9b69..5bb66bd 100644 --- a/cmd/certverify/main.go +++ b/cmd/certverify/main.go @@ -6,19 +6,38 @@ import ( "fmt" "io/ioutil" "os" + "time" "github.com/cloudflare/cfssl/helpers" + "github.com/cloudflare/cfssl/revoke" "github.com/kisom/die" "github.com/kisom/goutils/lib" ) +func printRevocation(cert *x509.Certificate) { + remaining := cert.NotAfter.Sub(time.Now()) + fmt.Printf("certificate expires in %s.\n", lib.Duration(remaining)) + + revoked, ok := revoke.VerifyCertificate(cert) + if !ok { + fmt.Fprintf(os.Stderr, "[!] the revocation check failed (failed to determine whether certificate\nwas revoked)") + return + } + + if revoked { + fmt.Fprintf(os.Stderr, "[!] the certificate has been revoked\n") + return + } +} + func main() { var caFile, intFile string - var forceIntermediateBundle, verbose bool + var forceIntermediateBundle, revexp, verbose bool flag.StringVar(&caFile, "ca", "", "CA certificate `bundle`") flag.StringVar(&intFile, "i", "", "intermediate `bundle`") flag.BoolVar(&forceIntermediateBundle, "f", false, "force the use of the intermediate bundle, ignoring any intermediates bundled with certificate") + flag.BoolVar(&revexp, "r", false, "print revocation and expiry information") flag.BoolVar(&verbose, "v", false, "verbose") flag.Parse() @@ -86,4 +105,8 @@ func main() { if verbose { fmt.Println("OK") } + + if revexp { + printRevocation(cert) + } } diff --git a/lib/lib.go b/lib/lib.go index e59a98a..1096e0b 100644 --- a/lib/lib.go +++ b/lib/lib.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "time" ) var progname = filepath.Base(os.Args[0]) @@ -81,3 +82,32 @@ func Itoa(i int, wid int) string { b[bp] = byte('0' + i) return string(b[bp:]) } + +var ( + dayDuration = 24 * time.Hour + yearDuration = (365 * dayDuration) + (6 * time.Hour) +) + +func Duration(d time.Duration) string { + var s string + if d >= yearDuration { + years := d / yearDuration + s += fmt.Sprintf("%dy", years) + d -= (years * yearDuration) + } + + if d >= dayDuration { + days := d / dayDuration + s += fmt.Sprintf("%dd", days) + } + + if s != "" { + return s + } + + d %= 1 * time.Second + hours := d / time.Hour + d -= (hours * time.Hour) + s += fmt.Sprintf("%dh%s", hours, d) + return s +}