Description
It would be useful to have a callback in tls.Config called after the TLS handshake to allow the callee to perform additional or alternate certificate verification on the peer. This would enable custom cert checking and/or explicit certificate pinning/whitelisting (e.g. by cert hash).
I propose calling VerifyPeerCertificate (if !nil) during the respective client and server handshakes (doFullHandshake and processPeerCertsFromClient) after the peer certificates have been received.
The func is introduced to tls.Config
// VerifyPeerCertificate returns an error if the peer should not be
// trusted. It will be called after the initial handshake and before
// any other verification checks on the cert or chain are performed.
// This provides the callee an opportunity to augment the certificate
// verification.
//
// If VerifyPeerCertificate is not nil and returns an error,
// then the handshake will fail.
VerifyPeerCertificate func(peer []*x509.Certificate) error
This works with both http and http2. It appears that tls.Config is cloned from the initial transport for TLSNextProto -- is this intentional or an artifact of the current impl?
c := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 60 * time.Second,
KeepAlive: 60 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
// inject our callback to check the peer here
NextProtos: []string{"h2"},
VerifyPeerCertificate: verifyPeerCertificate,
},
TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{
"h2": func(authority string, c *tls.Conn) http.RoundTripper {
return http.DefaultTransport
},
},
},
}
I have a changset ready if this is a reasonable approach.