func ExampleDial() { // Connecting with a custom root-certificate set. const rootPEM = ` -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7 qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY /iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx -----END CERTIFICATE-----` // First, create the set of root certificates. For this example we only // have one. It's also possible to omit this in order to use the // default root set of the current operating system. roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(rootPEM)) if !ok { panic("failed to parse root certificate") } conn, err := ztls.Dial("tcp", "mail.google.com:443", &ztls.Config{ RootCAs: roots, }) if err != nil { panic("failed to connect: " + err.Error()) } conn.Close() }
// Pre-main bind flags to variables func init() { flag.StringVar(&config.Encoding, "encoding", "string", "Encode banner as string|hex|base64") flag.StringVar(&outputFileName, "output-file", "-", "Output filename, use - for stdout") flag.StringVar(&inputFileName, "input-file", "-", "Input filename, use - for stdin") flag.StringVar(&metadataFileName, "metadata-file", "-", "File to record banner-grab metadata, use - for stdout") flag.StringVar(&logFileName, "log-file", "-", "File to log to, use - for stderr") flag.StringVar(&interfaceName, "interface", "", "Network interface to send on") flag.UintVar(&portFlag, "port", 80, "Port to grab on") flag.UintVar(&timeout, "timeout", 10, "Set connection timeout in seconds") flag.BoolVar(&config.TLS, "tls", false, "Grab over TLS") flag.StringVar(&tlsVersion, "tls-version", "", "Max TLS version to use (implies --tls)") flag.BoolVar(&udp, "udp", false, "Grab over UDP") flag.UintVar(&config.Senders, "senders", 1000, "Number of send coroutines to use") flag.UintVar(&config.ConnectionsPerHost, "connections-per-host", 1, "Number of times to connect to each host (results in more output)") flag.BoolVar(&config.Banners, "banners", false, "Read banner upon connection creation") flag.StringVar(&messageFileName, "data", "", "Send a message and read response (%s will be replaced with destination IP)") flag.StringVar(&config.HTTP.Endpoint, "http", "", "Send an HTTP request to an endpoint") flag.StringVar(&config.HTTP.Method, "http-method", "GET", "Set HTTP request method type") flag.StringVar(&config.HTTP.UserAgent, "http-user-agent", "", "Set a custom HTTP user agent") flag.StringVar(&config.HTTP.ProxyDomain, "http-proxy-domain", "", "Send a CONNECT <domain> first") flag.IntVar(&config.HTTP.MaxSize, "http-max-size", 256, "Max kilobytes to read in response to an HTTP request") flag.IntVar(&config.HTTP.MaxRedirects, "http-max-redirects", 0, "Max number of redirects to follow") flag.BoolVar(&config.TLSExtendedRandom, "tls-extended-random", false, "send extended random extension") flag.StringVar(&config.EHLODomain, "ehlo", "", "Send an EHLO with the specified domain (implies --smtp)") flag.BoolVar(&config.SMTPHelp, "smtp-help", false, "Send a SMTP help (implies --smtp)") flag.BoolVar(&config.StartTLS, "starttls", false, "Send STARTTLS before negotiating") flag.BoolVar(&config.SMTP, "smtp", false, "Conform to SMTP when reading responses and sending STARTTLS") flag.BoolVar(&config.IMAP, "imap", false, "Conform to IMAP rules when sending STARTTLS") flag.BoolVar(&config.POP3, "pop3", false, "Conform to POP3 rules when sending STARTTLS") flag.BoolVar(&config.Modbus, "modbus", false, "Send some modbus data") flag.BoolVar(&config.BACNet, "bacnet", false, "Send some BACNet data") flag.BoolVar(&config.Fox, "fox", false, "Send some Niagara Fox Tunneling data") flag.BoolVar(&config.S7, "s7", false, "Send some Siemens S7 data") flag.BoolVar(&config.NoSNI, "no-sni", false, "Do not send domain name in TLS handshake regardless of whether known") flag.BoolVar(&config.ExportsOnly, "export-ciphers", false, "Send only export ciphers") flag.BoolVar(&config.ExportsDHOnly, "export-dhe-ciphers", false, "Send only export DHE ciphers") flag.BoolVar(&config.DHEOnly, "dhe-ciphers", false, "Send only DHE ciphers (not ECDHE)") flag.BoolVar(&config.ChromeOnly, "chrome-ciphers", false, "Send Chrome Ordered Cipher Suites") flag.BoolVar(&config.ChromeNoDHE, "chrome-no-dhe-ciphers", false, "Send chrome ciphers minus DHE suites") flag.BoolVar(&config.FirefoxOnly, "firefox-ciphers", false, "Send Firefox Ordered Cipher Suites") flag.BoolVar(&config.SafariOnly, "safari-ciphers", false, "Send Safari Ordered Cipher Suites") flag.BoolVar(&config.SafariNoDHE, "safari-no-dhe-ciphers", false, "Send Safari ciphers minus DHE suites") flag.BoolVar(&config.Heartbleed, "heartbleed", false, "Check if server is vulnerable to Heartbleed (implies --tls)") flag.BoolVar(&config.GatherSessionTicket, "tls-session-ticket", false, "Send support for TLS Session Tickets and output ticket if presented") flag.BoolVar(&config.ExtendedMasterSecret, "tls-extended-master-secret", false, "Offer RFC 7627 Extended Master Secret extension") flag.BoolVar(&config.TLSVerbose, "tls-verbose", false, "Add extra TLS information to JSON output (client hello, client KEX, key material, etc)") flag.StringVar(&rootCAFileName, "ca-file", "", "List of trusted root certificate authorities in PEM format") flag.IntVar(&config.GOMAXPROCS, "gomaxprocs", 3, "Set GOMAXPROCS (default 3)") flag.BoolVar(&config.FTP, "ftp", false, "Read FTP banners") flag.BoolVar(&config.FTPAuthTLS, "ftp-authtls", false, "Collect FTPS certificates in addition to FTP banners") flag.BoolVar(&config.DNP3, "dnp3", false, "Read DNP3 banners") flag.BoolVar(&config.SSH.SSH, "ssh", false, "SSH scan") flag.StringVar(&config.SSH.Client, "ssh-client", "", "Mimic behavior of a specific SSH client") flag.StringVar(&config.SSH.KexAlgorithms, "ssh-kex-algorithms", "", "Set SSH Key Exchange Algorithms") flag.StringVar(&config.SSH.HostKeyAlgorithms, "ssh-host-key-algorithms", "", "Set SSH Host Key Algorithms") flag.StringVar(&config.SSH.FixedKexValue, "ssh-kex-value", "", "Set SSH DH kex value in hexadecimal") flag.BoolVar(&config.SSH.NegativeOne, "ssh-negative-one", false, "Set SSH DH kex value to -1 in the selected group") flag.BoolVar(&config.Telnet, "telnet", false, "Read telnet banners") flag.IntVar(&config.TelnetMaxSize, "telnet-max-size", 65536, "Max bytes to read for telnet banner") flag.Parse() // Validate Go Runtime config if config.GOMAXPROCS < 1 { zlog.Fatalf("Invalid GOMAXPROCS (must be at least 1, given %d)", config.GOMAXPROCS) } // Stop the lowliest idiot from using this to DoS people if config.ConnectionsPerHost > 50 || config.ConnectionsPerHost < 1 { zlog.Fatalf("--connections-per-host must be in the range [0,50]") } // Validate SSH related flags if config.SSH.SSH { if _, ok := config.SSH.GetClientImplementation(); !ok { zlog.Fatalf("Unknown SSH client %s", config.SSH.Client) } if _, err := config.SSH.MakeKexNameList(); err != nil { zlog.Fatalf("Bad SSH Key Exchange Algorithms: %s", err.Error()) } if _, err := config.SSH.MakeHostKeyNameList(); err != nil { zlog.Fatalf("Bad SSH Host Key Algorithms: %s", err.Error()) } if len(config.SSH.FixedKexValue) > 0 { b, err := hex.DecodeString(config.SSH.FixedKexValue) if err != nil { zlog.Fatalf("Bad SSH kex value (must be hex): %s", err.Error()) } config.SSH.FixedKexBytes = append([]byte{0x00}, b...) } } // Validate HTTP if config.HTTP.Method != "GET" && config.HTTP.Method != "HEAD" { zlog.Fatalf("Bad HTTP Method: %s. Valid options are: GET, HEAD.", config.HTTP.Method) } // Validate FTP if config.FTP && config.Banners { zlog.Fatal("--ftp and --banners are mutually exclusive") } if config.FTPAuthTLS && !config.FTP { zlog.Fatal("--ftp-authtls requires usage of --ftp") } // Validate Telnet if config.Telnet && config.Banners { zlog.Fatal("--telnet and --banners are mutually exclusive") } // Validate TLS Versions tv := strings.ToUpper(tlsVersion) if tv != "" { config.TLS = true } if config.TLS { switch tv { case "SSLV3", "SSLV30", "SSLV3.0": config.TLSVersion = ztls.VersionSSL30 tlsVersion = "SSLv3" case "TLSV1", "TLSV10", "TLSV1.0": config.TLSVersion = ztls.VersionTLS10 tlsVersion = "TLSv1.0" case "TLSV11", "TLSV1.1": config.TLSVersion = ztls.VersionTLS11 tlsVersion = "TLSv1.1" case "", "TLSV12", "TLSV1.2": config.TLSVersion = ztls.VersionTLS12 tlsVersion = "TLSv1.2" default: zlog.Fatal("Invalid SSL/TLS versions") } } // STARTTLS cannot be used with TLS if config.StartTLS && config.TLS { zlog.Fatal("Cannot both initiate a TLS and STARTTLS connection") } if config.EHLODomain != "" { config.EHLO = true } if config.SMTPHelp || config.EHLO { config.SMTP = true } if config.SMTP && (config.IMAP || config.POP3) { zlog.Fatal("Cannot conform to SMTP and IMAP/POP3 at the same time") } if config.IMAP && config.POP3 { zlog.Fatal("Cannot conform to IMAP and POP3 at the same time") } if config.EHLO && (config.IMAP || config.POP3) { zlog.Fatal("Cannot send an EHLO when conforming to IMAP or POP3") } if config.SMTP { mailType = "SMTP" } else if config.POP3 { mailType = "POP3" } else if config.IMAP { mailType = "IMAP" } // Heartbleed requires STARTTLS or TLS if config.Heartbleed && !(config.StartTLS || config.TLS) { zlog.Fatal("Must specify one of --tls or --starttls for --heartbleed") } encoding = strings.ToLower(config.Encoding) // Check output encoding switch encoding { case "string": case "base64": case "hex": default: zlog.Fatalf("Invalid encoding '%s'", config.Encoding) } config.Encoding = encoding // Validate port if portFlag > 65535 { zlog.Fatal("Port", portFlag, "out of range") } config.Port = uint16(portFlag) // Validate timeout config.Timeout = time.Duration(timeout) * time.Second // Validate senders if config.Senders == 0 { zlog.Fatal("Error: Need at least one sender") } // Check the network interface var err error // Look at CA file if rootCAFileName != "" { var fd *os.File if fd, err = os.Open(rootCAFileName); err != nil { zlog.Fatal(err) } caBytes, readErr := ioutil.ReadAll(fd) if readErr != nil { zlog.Fatal(err) } config.RootCAPool = x509.NewCertPool() ok := config.RootCAPool.AppendCertsFromPEM(caBytes) if !ok { zlog.Fatal("Could not read certificates from PEM file. Invalid PEM?") } } // Open input and output files switch inputFileName { case "-": inputFile = os.Stdin default: if inputFile, err = os.Open(inputFileName); err != nil { zlog.Fatal(err) } } switch outputFileName { case "-": outputConfig.OutputFile = os.Stdout default: if outputConfig.OutputFile, err = os.Create(outputFileName); err != nil { zlog.Fatal(err) } } // Open message file, if applicable if messageFileName != "" { if messageFile, err := os.Open(messageFileName); err != nil { zlog.Fatal(err) } else { buf := make([]byte, 1024) n, err := messageFile.Read(buf) config.SendData = true config.Data = buf[0:n] if err != nil && err != io.EOF { zlog.Fatal(err) } messageFile.Close() } } // Open metadata file if metadataFileName == "-" { metadataFile = os.Stdout } else { if metadataFile, err = os.Create(metadataFileName); err != nil { zlog.Fatal(err) } } // Open log file, attach to configs var logFile *os.File if logFileName == "-" { logFile = os.Stderr } else { if logFile, err = os.Create(logFileName); err != nil { zlog.Fatal(err) } } logger := zlog.New(logFile, "banner-grab") config.ErrorLog = logger }
func (hs *clientHandshakeState) doFullHandshake() error { c := hs.c msg, err := c.readHandshake() if err != nil { return err } var serverCert *x509.Certificate isAnon := hs.suite != nil && (hs.suite.flags&suiteAnon > 0) if !isAnon { certMsg, ok := msg.(*certificateMsg) if !ok || len(certMsg.certificates) == 0 { c.sendAlert(alertUnexpectedMessage) return unexpectedMessageError(certMsg, msg) } hs.finishedHash.Write(certMsg.marshal()) certs := make([]*x509.Certificate, len(certMsg.certificates)) invalidCert := false var invalidCertErr error for i, asn1Data := range certMsg.certificates { cert, err := x509.ParseCertificate(asn1Data) if err != nil { invalidCert = true invalidCertErr = err break } certs[i] = cert } c.handshakeLog.ServerCertificates = certMsg.MakeLog() if !invalidCert { opts := x509.VerifyOptions{ Roots: c.config.RootCAs, CurrentTime: c.config.time(), DNSName: c.config.ServerName, Intermediates: x509.NewCertPool(), } // Always check validity of the certificates for _, cert := range certs { /* if i == 0 { continue } */ opts.Intermediates.AddCert(cert) } var validation *x509.Validation c.verifiedChains, validation, err = certs[0].ValidateWithStupidDetail(opts) c.handshakeLog.ServerCertificates.addParsed(certs, validation) // If actually verifying and invalid, reject if !c.config.InsecureSkipVerify { if err != nil { c.sendAlert(alertBadCertificate) return err } } } if invalidCert { c.sendAlert(alertBadCertificate) return errors.New("tls: failed to parse certificate from server: " + invalidCertErr.Error()) } c.peerCertificates = certs if hs.serverHello.ocspStapling { msg, err = c.readHandshake() if err != nil { return err } cs, ok := msg.(*certificateStatusMsg) if !ok { c.sendAlert(alertUnexpectedMessage) return unexpectedMessageError(cs, msg) } hs.finishedHash.Write(cs.marshal()) if cs.statusType == statusTypeOCSP { c.ocspResponse = cs.response } } serverCert = certs[0] var supportedCertKeyType bool switch serverCert.PublicKey.(type) { case *rsa.PublicKey, *ecdsa.PublicKey: supportedCertKeyType = true break case *dsa.PublicKey: if c.config.ClientDSAEnabled { supportedCertKeyType = true } default: break } if !supportedCertKeyType { c.sendAlert(alertUnsupportedCertificate) return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", serverCert.PublicKey) } msg, err = c.readHandshake() if err != nil { return err } } // If we don't support the cipher, quit before we need to read the hs.suite // variable if c.cipherError != nil { return c.cipherError } skx, ok := msg.(*serverKeyExchangeMsg) keyAgreement := hs.suite.ka(c.vers) if ok { hs.finishedHash.Write(skx.marshal()) err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, serverCert, skx) c.handshakeLog.ServerKeyExchange = skx.MakeLog(keyAgreement) if err != nil { c.sendAlert(alertUnexpectedMessage) return err } msg, err = c.readHandshake() if err != nil { return err } } var chainToSend *Certificate var certRequested bool certReq, ok := msg.(*certificateRequestMsg) if ok { certRequested = true // RFC 4346 on the certificateAuthorities field: // A list of the distinguished names of acceptable certificate // authorities. These distinguished names may specify a desired // distinguished name for a root CA or for a subordinate CA; // thus, this message can be used to describe both known roots // and a desired authorization space. If the // certificate_authorities list is empty then the client MAY // send any certificate of the appropriate // ClientCertificateType, unless there is some external // arrangement to the contrary. hs.finishedHash.Write(certReq.marshal()) var rsaAvail, ecdsaAvail bool for _, certType := range certReq.certificateTypes { switch certType { case certTypeRSASign: rsaAvail = true case certTypeECDSASign: ecdsaAvail = true } } // We need to search our list of client certs for one // where SignatureAlgorithm is RSA and the Issuer is in // certReq.certificateAuthorities findCert: for i, chain := range c.config.Certificates { if !rsaAvail && !ecdsaAvail { continue } for j, cert := range chain.Certificate { x509Cert := chain.Leaf // parse the certificate if this isn't the leaf // node, or if chain.Leaf was nil if j != 0 || x509Cert == nil { if x509Cert, err = x509.ParseCertificate(cert); err != nil { c.sendAlert(alertInternalError) return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) } } switch { case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: default: continue findCert } if len(certReq.certificateAuthorities) == 0 { // they gave us an empty list, so just take the // first RSA cert from c.config.Certificates chainToSend = &chain break findCert } for _, ca := range certReq.certificateAuthorities { if bytes.Equal(x509Cert.RawIssuer, ca) { chainToSend = &chain break findCert } } } } msg, err = c.readHandshake() if err != nil { return err } } shd, ok := msg.(*serverHelloDoneMsg) if !ok { c.sendAlert(alertUnexpectedMessage) return unexpectedMessageError(shd, msg) } hs.finishedHash.Write(shd.marshal()) // If the server requested a certificate then we have to send a // Certificate message, even if it's empty because we don't have a // certificate to send. if certRequested { certMsg := new(certificateMsg) if chainToSend != nil { certMsg.certificates = chainToSend.Certificate } hs.finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) } preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, serverCert) if err != nil { c.sendAlert(alertInternalError) return err } if ckx != nil { hs.finishedHash.Write(ckx.marshal()) c.writeRecord(recordTypeHandshake, ckx.marshal()) } if chainToSend != nil { var signed []byte certVerify := &certificateVerifyMsg{ hasSignatureAndHash: c.vers >= VersionTLS12, } // Determine the hash to sign. var signatureType uint8 switch c.config.Certificates[0].PrivateKey.(type) { case *ecdsa.PrivateKey: signatureType = signatureECDSA case *rsa.PrivateKey: signatureType = signatureRSA default: c.sendAlert(alertInternalError) return errors.New("unknown private key type") } certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, c.config.signatureAndHashesForClient(), signatureType) if err != nil { c.sendAlert(alertInternalError) return err } digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret) if err != nil { c.sendAlert(alertInternalError) return err } switch key := c.config.Certificates[0].PrivateKey.(type) { case *ecdsa.PrivateKey: var r, s *big.Int r, s, err = ecdsa.Sign(c.config.rand(), key, digest) if err == nil { signed, err = asn1.Marshal(ecdsaSignature{r, s}) } case *rsa.PrivateKey: signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest) default: err = errors.New("unknown private key type") } if err != nil { c.sendAlert(alertInternalError) return errors.New("tls: failed to sign handshake with client certificate: " + err.Error()) } certVerify.signature = signed hs.writeClientHash(certVerify.marshal()) c.writeRecord(recordTypeHandshake, certVerify.marshal()) } var cr, sr []byte if hs.hello.extendedRandomEnabled { helloRandomLen := len(hs.hello.random) helloExtendedRandomLen := len(hs.hello.extendedRandom) cr = make([]byte, helloRandomLen+helloExtendedRandomLen) copy(cr, hs.hello.random) copy(cr[helloRandomLen:], hs.hello.extendedRandom) } else { cr = hs.hello.random } if hs.serverHello.extendedRandomEnabled { serverRandomLen := len(hs.serverHello.random) serverExtendedRandomLen := len(hs.serverHello.extendedRandom) sr = make([]byte, serverRandomLen+serverExtendedRandomLen) copy(sr, hs.serverHello.random) copy(sr[serverRandomLen:], hs.serverHello.extendedRandom) } else { sr = hs.serverHello.random } hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, cr, sr) return nil }
// processCertsFromClient takes a chain of client certificates either from a // Certificates message or from a sessionState and verifies them. It returns // the public key of the leaf certificate. func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { c := hs.c hs.certsFromClient = certificates certs := make([]*x509.Certificate, len(certificates)) var err error for i, asn1Data := range certificates { if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { c.sendAlert(alertBadCertificate) return nil, errors.New("tls: failed to parse client certificate: " + err.Error()) } } if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { opts := x509.VerifyOptions{ Roots: c.config.ClientCAs, CurrentTime: c.config.time(), Intermediates: x509.NewCertPool(), KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } for _, cert := range certs[1:] { opts.Intermediates.AddCert(cert) } chains, err := certs[0].Verify(opts) if err != nil { c.sendAlert(alertBadCertificate) return nil, errors.New("tls: failed to verify client's certificate: " + err.Error()) } ok := false for _, ku := range certs[0].ExtKeyUsage { if ku == x509.ExtKeyUsageClientAuth { ok = true break } } if !ok { c.sendAlert(alertHandshakeFailure) return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") } c.verifiedChains = chains } if len(certs) > 0 { var pub crypto.PublicKey switch key := certs[0].PublicKey.(type) { case *ecdsa.PublicKey, *rsa.PublicKey: pub = key default: c.sendAlert(alertUnsupportedCertificate) return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey) } c.peerCertificates = certs return pub, nil } return nil, nil }
func ExampleCertificate_Verify() { // Verifying with a custom list of root certificates. const rootPEM = ` -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7 qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY /iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx -----END CERTIFICATE-----` const certPEM = ` -----BEGIN CERTIFICATE----- MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q 5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC 7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+ gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283 TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq 0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA== -----END CERTIFICATE-----` // First, create the set of root certificates. For this example we only // have one. It's also possible to omit this in order to use the // default root set of the current operating system. roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(rootPEM)) if !ok { panic("failed to parse root certificate") } block, _ := pem.Decode([]byte(certPEM)) if block == nil { panic("failed to parse certificate PEM") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { panic("failed to parse certificate: " + err.Error()) } opts := x509.VerifyOptions{ DNSName: "mail.google.com", Roots: roots, } if _, err := cert.Verify(opts); err != nil { panic("failed to verify certificate: " + err.Error()) } }