diff --git a/lib/ocsp.go b/lib/ocsp.go index dbc4455..de61b8f 100644 --- a/lib/ocsp.go +++ b/lib/ocsp.go @@ -27,11 +27,37 @@ import ( "net/url" "strings" + "github.com/fatih/color" "golang.org/x/crypto/ocsp" ) var ( skippedRevocationCheck = errors.New("skipped revocation check") + + revocationStatusColor = map[int]*color.Color{ + ocsp.Good: green, + ocsp.Revoked: red, + ocsp.Unknown: yellow, + } + + revocationStatusDescription = map[int]string{ + ocsp.Good: "Good", + ocsp.Revoked: "Revoked", + ocsp.Unknown: "Unknown", + } + + revocationReasonDescription = map[int]string{ + ocsp.Unspecified: "Unspecified", + ocsp.KeyCompromise: "KeyCompromise", + ocsp.CACompromise: "CACompromise", + ocsp.AffiliationChanged: "AffiliationChanged", + ocsp.Superseded: "Superseded", + ocsp.CessationOfOperation: "CessationOfOperation", + ocsp.CertificateHold: "CertificateHold", + ocsp.RemoveFromCRL: "RemoveFromCRL", + ocsp.PrivilegeWithdrawn: "PrivilegeWithdrawn", + ocsp.AACompromise: "AACompromise", + } ) func checkOCSP(chain []*x509.Certificate, ocspStaple []byte) (status *ocsp.Response, err error) { diff --git a/lib/verify.go b/lib/verify.go index 150a34a..0a33303 100644 --- a/lib/verify.go +++ b/lib/verify.go @@ -27,7 +27,6 @@ import ( "crypto/tls" - "github.com/fatih/color" "golang.org/x/crypto/ocsp" ) @@ -39,10 +38,11 @@ type simpleVerifyCert struct { } type SimpleVerification struct { - Error string `json:"error,omitempty"` - OCSPStatus *ocsp.Response `json:"ocsp_response,omitempty"` - OCSPError string `json:"ocsp_error,omitempty"` - Chains [][]simpleVerifyCert `json:"chains"` + Error string `json:"error,omitempty"` + OCSPStatus *ocsp.Response `json:"ocsp_response,omitempty"` + OCSPWasStapled bool `json:"ocsp_was_stapled,omitempty"` + OCSPError string `json:"ocsp_error,omitempty"` + Chains [][]simpleVerifyCert `json:"chains"` } type SimpleResult struct { @@ -111,7 +111,8 @@ func caBundle(caPath string) *x509.CertPool { func VerifyChain(certs []*x509.Certificate, ocspStaple []byte, dnsName, caPath string) SimpleVerification { result := SimpleVerification{ - Chains: [][]simpleVerifyCert{}, + Chains: [][]simpleVerifyCert{}, + OCSPWasStapled: ocspStaple != nil, } intermediates := x509.NewCertPool() @@ -176,26 +177,12 @@ func PrintVerifyResult(out io.Writer, result SimpleVerification) { fmt.Fprintf(out, "\t%s\n", result.Error) return } - if result.OCSPError != "" { - fmt.Fprintf(out, red.SprintfFunc()("Certificate has OCSP servers, but was unable to check status:\n")) - fmt.Fprintf(out, "\t%s\n\n", result.OCSPError) - } else if result.OCSPStatus != nil { - var text string - var color *color.Color - switch result.OCSPStatus.Status { - case ocsp.Good: - text = "Good" - color = green - case ocsp.Revoked: - text = "Revoked" - color = red - default: - text = "Unknown" - color = yellow - } - fmt.Fprintf(out, color.SprintfFunc()("Checked OCSP status for certificate, got status:")) - fmt.Fprintf(out, "\n\t%s (last update: %s)\n\n", text, result.OCSPStatus.ProducedAt.Format(time.UnixDate)) - } + + printOCSPStatus(out, result) + printCertificateChains(out, result) +} + +func printCertificateChains(out io.Writer, result SimpleVerification) { fmt.Fprintf(out, green.SprintfFunc()("Found %d valid certificate chain(s):\n", len(result.Chains))) for i, chain := range result.Chains { fmt.Fprintf(out, "[%d] %s\n", i, fmtCert(chain[0])) @@ -207,3 +194,42 @@ func PrintVerifyResult(out io.Writer, result SimpleVerification) { } } } + +func printOCSPStatus(out io.Writer, result SimpleVerification) { + if result.OCSPError != "" { + fmt.Fprintf(out, red.SprintfFunc()("Certificate has OCSP extension, but was unable to check status:\n")) + fmt.Fprintf(out, "\t%s\n\n", result.OCSPError) + return + } + + if result.OCSPStatus != nil { + status, ok := revocationStatusDescription[result.OCSPStatus.Status] + if !ok { + status = "Unknown" + } + + color, ok := revocationStatusColor[result.OCSPStatus.Status] + if !ok { + color = yellow + } + + wasStapled := "" + if result.OCSPWasStapled { + wasStapled = " (was stapled)" + } + + fmt.Fprintf(out, color.SprintfFunc()("Checked OCSP status for certificate%s, got:", wasStapled)) + fmt.Fprintf(out, "\n\t%s (last update: %s)", status, result.OCSPStatus.ProducedAt.Format(time.RFC822)) + + if result.OCSPStatus.Status == ocsp.Revoked { + reason, ok := revocationReasonDescription[result.OCSPStatus.RevocationReason] + if !ok { + reason = "Unknown" + } + + fmt.Fprintf(out, "\n\tWas revoked at %s due to: %s", result.OCSPStatus.RevokedAt.Format(time.RFC822), reason) + } + + fmt.Fprintf(out, "\n\n") + } +}