How to override/extend x509.CertPool contains function?

Hello. Is there some way to configure Server TLS so that when it is verifying a client certificate it can call out to my own function when the client certificate is not found in the server’s tls.Config ClientCAs x509.CertPool?

Poking around in the Go source i can see it gets to certs[0].Verify(opts) in go/handshake_server.go at master · golang/go · GitHub, and in Verify it calls if opts.Roots.contains(c) and in the contains function what i need is some way to have some custom code if s.havesum is false - go/cert_pool.go at 527ace0ffa81d59698d3a78ac3545de7295ea76b · golang/go · GitHub

What I’m trying to do is have the Server accept clients using self signed certificates that are registered in our backend database.

Thanks for any comments.

You don’t need that.
Here’s an example of how to do it:

package main

import (
	"crypto/tls"
	"log"
	"net/http"
)

var tlsConf = &tls.Config{GetCertificate: processCert}

func main() {
	srv := &http.Server{TLSConfig: tlsConf}
	cert, err := tls.LoadX509KeyPair("cert", "privkey") // In case of multiple certificate imports
	if err != nil {
		log.Panic(err.Error())
	}
	tlsConf.Certificates = append(tlsConf.Certificates, cert)
	err = srv.ListenAndServeTLS("", "") // Can specify certs here directly, too

	if err != nil {
		log.Fatal(err)
	}
}

func processCert(hello *tls.ClientHelloInfo) (crt *tls.Certificate, err error) {
	if isValidCert(hello) {
		log.Println("Valid certificate")
		// Do whatever else
	}
	return
}

func isValidCert(hello *tls.ClientHelloInfo) (supported bool) {
	for _, cert := range tlsConf.Certificates {
		cert := cert
		if err := hello.SupportsCertificate(&cert); err == nil {
			return true
		}
	}

	return
}

Just add the self-signed certificate to both sides.

Sorry, I don’t follow how that helps? I want to be able to have the server side use a function to validate the client certificate - I don’t see how to get hold of the client certifcate from *tls.ClientHelloInfo?

(the client hasn’t even sent its client certificate at the point where processCert gets called has it?)

If you implement the VerifyPeerCertificate method in tlsConf, that would only receive the certificate data when there is actually a client certificate sent, but not for any other.

On the other hand, processCert is called every time a new TLS handshake request has been received, thus making you able to block invalid requests at the very first level of the TLS handshake process.

But anyway if you need the first one, here’s an example of this:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"log"
	"net/http"
	"time"
)

var tlsConf = &tls.Config{VerifyPeerCertificate: validateCert}

func main() {
	srv := &http.Server{TLSConfig: tlsConf}
	cert, err := tls.LoadX509KeyPair("cert", "privkey") // In case of multiple certificate imports
	if err != nil {
		log.Panic(err.Error())
	}
	tlsConf.Certificates = append(tlsConf.Certificates, cert)
	err = srv.ListenAndServeTLS("", "") // Can specify certs here directly, too

	if err != nil {
		log.Fatal(err)
	}
}

func validateCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
	if verifiedChains == nil {
		return errors.New("client didn't send any certificates")
	}

	for _, chain := range verifiedChains {
		for _, cert := range chain {
			// implement any sort of validations on the cert
			if time.Now().After(cert.NotAfter) { // an example certificate expiration check
				return errors.New("certificate expired")
			}
		}
	}
	return nil
}

Thanks. I got it to do what I needed using that VerifyPeerCertificate along with setting ClientAuth to tls.RequireAnyClientCert:

var tlsConf = &tls.Config{
   VerifyPeerCertificate: validateCert,
   ClientAuth:            tls.RequireAnyClientCert, 
}