// Dial initiates a TLS connection to an outbound server. It returns a // TLS connection to the server. func Dial(address string, tr *Transport) (*tls.Conn, error) { host, _, err := net.SplitHostPort(address) if err != nil { // Assume address is a hostname, and that it should // use the HTTPS port number. host = address address = net.JoinHostPort(address, "443") } cfg, err := tr.TLSClientAuthClientConfig(host) if err != nil { return nil, err } conn, err := tls.Dial("tcp", address, cfg) if err != nil { return nil, err } state := conn.ConnectionState() if len(state.VerifiedChains) == 0 { return nil, errors.New(errors.CertificateError, errors.VerifyFailed) } for _, chain := range state.VerifiedChains { for _, cert := range chain { revoked, ok := revoke.VerifyCertificate(cert) if (!tr.RevokeSoftFail && !ok) || revoked { return nil, errors.New(errors.CertificateError, errors.VerifyFailed) } } } return conn, nil }
func chainValidation(addr, hostname string) (grade Grade, output Output, err error) { chain, err := getChain(addr, defaultTLSConfig(hostname)) if err != nil { return } var warnings []string for i := 0; i < len(chain)-1; i++ { cert, parent := chain[i], chain[i+1] valid := helpers.ValidExpiry(cert) if !valid { warnings = append(warnings, fmt.Sprintf("Certificate for %s is valid for too long", cert.Subject.CommonName)) } revoked, ok := revoke.VerifyCertificate(cert) if !ok { warnings = append(warnings, fmt.Sprintf("couldn't check if %s is revoked", cert.Subject.CommonName)) } if revoked { err = fmt.Errorf("%s is revoked", cert.Subject.CommonName) return } if !parent.IsCA { err = fmt.Errorf("%s is not a CA", parent.Subject.CommonName) return } if !bytes.Equal(cert.AuthorityKeyId, parent.SubjectKeyId) { err = fmt.Errorf("%s AuthorityKeyId differs from %s SubjectKeyId", cert.Subject.CommonName, parent.Subject.CommonName) return } if err = cert.CheckSignatureFrom(parent); err != nil { return } switch cert.SignatureAlgorithm { case x509.ECDSAWithSHA1: warnings = append(warnings, fmt.Sprintf("%s is signed by ECDSAWithSHA1", cert.Subject.CommonName)) case x509.SHA1WithRSA: warnings = append(warnings, fmt.Sprintf("%s is signed by RSAWithSHA1", cert.Subject.CommonName)) } } if len(warnings) == 0 { grade = Good } else { grade = Warning output = warnings } return }
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 } }
// worker does all the parsing and validation of the certificate(s) // contained in a single file. It first reads all the data in the // file, then begins parsing certificates in the file. Those // certificates are then checked for revocation. func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) { defer (*pool).Done() for { path, ok := <-paths if !ok { return } log.Infof("Loading %s", path) fileData, err := ioutil.ReadFile(path) if err != nil { log.Warningf("%v", err) continue } for { var block *pem.Block if len(fileData) == 0 { break } block, fileData = pem.Decode(fileData) if block == nil { log.Warningf("%s: no PEM data found", path) break } else if block.Type != "CERTIFICATE" { log.Info("Skipping non-certificate") continue } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { log.Warningf("Invalid certificate: %v", err) continue } log.Infof("Validating %+v", cert.Subject) revoked, ok := revoke.VerifyCertificate(cert) if !ok { log.Warning("Failed to verify certificate.") } else if !revoked { bundler <- cert } else { log.Info("Skipping revoked certificate") } } } }