How to get client certificates in Go HTTPS server
The client shouldn't send a certificate unless requested. Set ClientAuth
in the tls.Config
to an appropriate tls.ClientAuthType
.
For example, to only request that a client send a certificate, you can use:
server := &http.Server{
Addr: ":8080",
TLSConfig: &tls.Config{
ClientAuth: tls.RequestClientCert,
},
}
server.ListenAndServeTLS("server.crt", "server.key")
Valentin
Updated on June 04, 2022Comments
-
Valentin almost 2 years
I'm trying to understand how to get client's certificates in Go web server. Here is a server code:
package main import ( "log" "net/http" "net/http/httputil" ) func defaultHandler(w http.ResponseWriter, r *http.Request) { dump, err := httputil.DumpRequest(r, true) log.Println("HTTP request", r, string(dump), err) log.Println("HTTP TLS", r.TLS, string(r.TLS.TLSUnique)) certs := r.TLS.PeerCertificates log.Println("HTTP CERTS", certs) w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Hello")) } func main() { http.HandleFunc("/", defaultHandler) http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil) }
and here is client code
package main import ( "crypto/tls" "io/ioutil" "log" "net/http" "os" ) func HttpClient() (client *http.Client) { uckey := os.Getenv("X509_USER_KEY") ucert := os.Getenv("X509_USER_CERT") x509cert, err := tls.LoadX509KeyPair(ucert, uckey) if err != nil { panic(err.Error()) } certs := []tls.Certificate{x509cert} if len(certs) == 0 { client = &http.Client{} return } tr := &http.Transport{ TLSClientConfig: &tls.Config{Certificates: certs, InsecureSkipVerify: true}, } client = &http.Client{Transport: tr} return } func main() { rurl := "https://localhost:8080" client := HttpClient() req, err := http.NewRequest("GET", rurl, nil) if err != nil { log.Println("Unable to make GET request", err) os.Exit(1) } req.Header.Add("Accept", "*/*") resp, err := client.Do(req) if err != nil { log.Println(err) os.Exit(1) } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) log.Println(string(data)) }
If I run both server and a client I see the following on a server side:
2017/02/08 15:46:49 HTTP request &{GET / HTTP/1.1 1 1 map[User-Agent:[Go-http-client/1.1] Accept:[*/*] Accept-Encoding:[gzip]] {} 0 [] false localhost:8080 map[] map[] <nil> map[] 127.0.0.1:58941 / 0xc4204ef080 <nil> <nil> 0xc420014d40} GET / HTTP/1.1 Host: localhost:8080 Accept: */* Accept-Encoding: gzip User-Agent: Go-http-client/1.1 <nil> 2017/02/08 15:46:49 HTTP TLS &{771 true false 49195 true localhost [] [] [] [] [203 144 196 105 155 216 89 105 83 90 93 4]} ːiSZ] 2017/02/08 15:46:49 HTTP CERTS []
As you can see the client's certificates are empty.
While if I invoke curl call to a server providing my certificates, then I can see server certificates:
curl -L -k --key mykey.key --cert mycert.pem -vvv https://localhost:8080 * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) * ALPN, offering http/1.1 * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH * successfully set certificate verify locations: * CAfile: /opt/local/share/curl/curl-ca-bundle.crt CApath: none * TLSv1.2 (OUT), TLS header, Certificate Status (22): * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Client hello (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS change cipher, Client hello (1): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256 * ALPN, server accepted to use http/1.1 * Server certificate: * subject: C=US; ST=NY; L=Town; O=Bla-Bla * start date: Feb 8 14:12:06 2017 GMT * expire date: Feb 6 14:12:06 2027 GMT * issuer: C=US; ST=NY; L=Ithaca; O=Cornell * SSL certificate verify result: self signed certificate (18), continuing anyway. > GET / HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.52.1 > Accept: */*
As you can see SSL negotiation is in place and curl client successfully reports server certificate. What I need is to access client's certificate on a server side to do proper authentication. But so far I can't see any client's certificate.
Any help is really welcome. Thanks, Valentin.
-
bithavoc over 5 yearsFYI, this requests the client to present a certificate, that doesn't mean it's a valid certificate. you probably need
RequireAndVerifyClientCert
, here's a full example: gist.github.com/xjdrew/97be3811966c8300b724deabc10e38e2 -
Nick Roz over 3 yearswhat about mtls?
-
JimB over 3 years@NickRoz: what is the question about mTLS? There are multiple
ClientAuthType
values, and there is aClientCAs
field in thetls.Config
.