diff --git a/main.go b/main.go index de663a1..4b53327 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,11 @@ var ( connectName = connect.Flag("name", "Override the server name used for Server Name Indication (SNI).").String() connectCaPath = connect.Flag("ca", "Path to CA bundle (system default if unspecified).").ExistingFile() connectPem = connect.Flag("pem", "Write output as PEM blocks instead of human-readable format.").Bool() + + verify = app.Command("verify", "Verify a certificate chain from file/stdin against a name.") + verifyFile = verify.Arg("file", "Certificate file to dump (or stdin if not specified).").ExistingFile() + verifyName = verify.Flag("name", "Server name to verify certificate against.").Required().String() + verifyCaPath = verify.Flag("ca", "Path to CA bundle (system default if unspecified).").ExistingFile() ) const ( @@ -144,9 +149,50 @@ func main() { } verifyChain(conn.ConnectionState().PeerCertificates, hostname, *connectCaPath) } + case verify.FullCommand(): + file := inputFile(*verifyFile) + defer file.Close() + + chain := []*x509.Certificate{} + readCerts([]*os.File{file}, func(block *pem.Block) { + switch block.Type { + case "CERTIFICATE": + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + fmt.Fprintf(os.Stderr, "error reading cert: %s", err) + os.Exit(1) + } + chain = append(chain, cert) + case "PKCS7": + certs, err := pkcs7.ExtractCertificates(block.Bytes) + if err != nil { + fmt.Fprintf(os.Stderr, "error parsing PKCS7 block: %s\n", err) + os.Exit(1) + } + chain = append(chain, certs...) + } + }) + + valid := verifyChain(chain, *verifyName, *verifyCaPath) + if !valid { + os.Exit(1) + } } } +func inputFile(fileName string) *os.File { + if fileName == "" { + return os.Stdin + } + + rawFile, err := os.Open(fileName) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to open file: %s\n", err) + os.Exit(1) + } + return rawFile +} + func inputFiles(fileNames []string) []*os.File { files := []*os.File{} if fileNames != nil { diff --git a/verify.go b/verify.go index 789fb8c..b2e36c1 100644 --- a/verify.go +++ b/verify.go @@ -40,7 +40,7 @@ func caBundle(caPath string) *x509.CertPool { return bundle } -func verifyChain(certs []*x509.Certificate, dnsName, caPath string) { +func verifyChain(certs []*x509.Certificate, dnsName, caPath string) bool { intermediates := x509.NewCertPool() for i := 1; i < len(certs); i++ { intermediates.AddCert(certs[i]) @@ -56,7 +56,7 @@ func verifyChain(certs []*x509.Certificate, dnsName, caPath string) { if err != nil { red.Printf("Failed to verify certificate chain:\n") fmt.Printf("\t%s\n", err) - return + return false } green.Printf("Server certificates appear to be valid (found %d chains):\n", len(chains)) @@ -84,6 +84,7 @@ func verifyChain(certs []*x509.Certificate, dnsName, caPath string) { } fmt.Printf("[%d] %s\n", i, strings.Join(names, "\n\t=> ")) } + return true } func isSelfSigned(cert *x509.Certificate) bool {