如何在 Go HTTPS 服务器中获取客户端证书

Posted

技术标签:

【中文标题】如何在 Go HTTPS 服务器中获取客户端证书【英文标题】:How to get client certificates in Go HTTPS server 【发布时间】:2017-06-26 16:27:01 【问题描述】:

我正在尝试了解如何在 Go 网络服务器中获取客户端的证书。这是一个服务器代码:

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)

这里是客户端代码

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.Certificatex509cert
    if len(certs) == 0 
       client = &http.Client
       return
    
    tr := &http.Transport
        TLSClientConfig: &tls.ConfigCertificates: certs,
        InsecureSkipVerify: true,
    
    client = &http.ClientTransport: 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))

如果我同时运行服务器和客户端,我会在服务器端看到以下内容:

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 []

如您所见,客户端的证书是空的。

如果我对提供我的证书的服务器调用 curl 调用,那么我可以看到服务器证书:

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: */*

如您所见,SSL 协商已就位,并且 curl 客户端成功报告服务器证书。我需要的是在 服务器端 访问客户端的证书以进行正确的身份验证。但到目前为止我看不到任何客户的证书。

非常欢迎任何帮助。 谢谢, 瓦伦丁。

【问题讨论】:

【参考方案1】:

除非请求,否则客户端不应发送证书。将tls.Config 中的ClientAuth 设置为适当的tls.ClientAuthType

例如,仅请求客户端发送证书,您可以使用:

server := &http.Server
    Addr: ":8080",
    TLSConfig: &tls.Config
        ClientAuth: tls.RequestClientCert,
    ,


server.ListenAndServeTLS("server.crt", "server.key")

【讨论】:

仅供参考,这要求客户出示证书,这并不意味着它是有效的证书。你可能需要RequireAndVerifyClientCert,这是一个完整的例子:gist.github.com/xjdrew/97be3811966c8300b724deabc10e38e2 mtls 呢? @NickRoz:关于 mTLS 的问题是什么? ClientAuthType有多个值,tls.Config中有一个ClientCAs字段。

以上是关于如何在 Go HTTPS 服务器中获取客户端证书的主要内容,如果未能解决你的问题,请参考以下文章

go and https

Charles抓包

Charles抓包

Charles抓包

无论证书有效性如何,我的 node.js https 客户端始终有效

如何获取颁发者证书的指纹或公钥?