在http.NewRequest中检查服务器SSL/TLS证书的指纹

5

在golang中进行http请求时,如何检查服务器SSL/TLS证书的指纹?

这段ruby代码展示了我想用go实现的功能:

  @verify_callback = proc do |preverify_ok, store_context|
    if preverify_ok and store_context.error == 0
      certificate = OpenSSL::X509::Certificate.new(store_context.chain[0])
      fingerprint = Digest::SHA1.hexdigest(certificate.to_der).upcase.scan(/../).join(":")
      $valid_fingerprints.include?(fingerprint)
    else
      false
    end
  end
2个回答

5
通常,在Go语言中生成证书指纹的过程相当简单。如果您已经有一个存储在cert 中的x509.Certificate结构体,您只需要执行以下操作:
sha1Fingerprint := sha1.Sum(cert.Raw)

在请求完成后从HTTP响应结构中获取证书也很容易(使用resp.TLS.PeerCertificates),但是这似乎不是您需要的。
如果您需要在TLS连接设置时访问服务器的证书,我认为您需要创建自己的http.Transport并将其交给DialTLS的自定义实现。然后,在配置http.Client以进行出站请求时,您将使用该传输。
在自定义的DialTLS函数中,您可以访问连接状态信息,如服务器的证书链,并可以从那里执行SHA1指纹生成。

我有一个包含Leaf(x509.Certificate)的自签名TLS证书(tls.Certificate)。我在tls.Config中使用tls.Certificate作为http.Server的配置。但是,使用sha1.Sum(cert.Leaf.Raw)计算出的指纹与浏览器显示的指纹不匹配。我在这里做错了什么? - phlow
sha1.Sum(cert.Certificate[0]) 已经工作,其中 "derBytes" 是链中唯一的元素。 - phlow

0

您可能不应该自己实现证书检查,而是让net/http根据您提供的有效CA进行检查。此外,通常直接使用指纹并不值得麻烦。

例如,这是如何设置要求客户端使用证书进行身份验证的HTTPS服务器。客户端证书必须由CA签名,否则SSL/TLS握手将停止。

    // Server's own certificate & key
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
       panic(err)
    }

    // Load the CA certificate(s)
    capool := x509.NewCertPool()
    cacert, err := ioutil.ReadFile("ca.crt")
    if err != nil {
       panic(err)
    }
    capool.AppendCertsFromPEM(cacert)

    // Server configuration
    config := tls.Config{Certificates: []tls.Certificate{cert}, ClientCAs: capool, ClientAuth: tls.RequireAndVerifyClientCert}
    config.NextProtos = []string{"http/1.1"}
    config.Rand = rand.Reader // Strictly not necessary, should be default

    // TLS web server
    myTLSWebServer := &http.Server{Addr: "myaddress", TLSConfig: &config, Handler: nil}

    // .. proceed with setting handlers etc
    http.HandleFunc("/", myHandler)

    // Bind to port and start the server up
    conn, err := net.Listen("tcp", settings.ServiceAddress)
    if err != nil {
       panic(err)
    }
    tlsListener := tls.NewListener(conn, &config)
    myTLSWebServer.Serve(tlsListener)

阅读tls.Config的文档,您会发现通过更改参数(ClientAuth、ClientCAs、Certificates、RootCAs),您可以轻松选择不同的证书检查模式。通常,您会在error中返回失败。
如果您坚持要检查指纹,您可以从请求TLS *tls.ConnectionState中检索TLS状态。我认为您应该使用该结构体中的Signature进行指纹识别。大致上,您可以这样做:
func lol(r *http.Request) {
    tls := r.TLS
    if tls != nil {
       // Try the first one for simplicity
       cert := tls.PeerCertificates[0]
       signature := cert.Signature
       // Do something with the signature
    }
}

应该可以解决问题。


1
我认为“如何在Go中计算证书指纹”仍然是一个有效的问题。 :) - ahmet alp balkan
承认指纹计算是这个问题的重要部分。请查看我的答案以获取解决方案。此答案中查看cert.Signature的建议是不正确的。 - amb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接