使用 golang 和 tls,如何将 rawCerts [][]byte(传递给 VerifyPeerCertificate)转换为 x509.Certificate?

With golang and tls, how to convert rawCerts [][]byte (passed to VerifyPeerCertificate) to an x509.Certificate?

上下文:我想建立到服务器的 TLS 连接,该服务器具有不同(但已知!)域的证书。

所以我想使用 tls.Dial('tcp', 'real-domain', conf),但验证证书的域就好像它是另一个域(我们称之为 wrong-domain),其中我知道服务器应该 return它。

所以我认为这样做的方法是在客户端 tls.Config 中覆盖 VerifyPeerCertificate

VerifyPeerCertificate 获取 rawCerts [][]byte 作为参数,但要使用 x509.Verify 我需要证书作为 x509.Certificate 类型。

问题是:如何将作为参数传递给VerifyPeerCertificaterawCerts [][]byte转换为x509.Certificate

我想这样使用它:

    conf := &tls.Config{
        VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
            verifyOptions := x509.VerifyOptions{
                DNSName:                   "wrong-domain",
                Roots:                     serverCertPool,
            }
            cert := &x509.Certificate{???} // How do I get the x509.Certificate out of the rawCerts [][]byte parameter?
            _, err := cert.Verify(verifyOptions)
            return err
        },
        InsecureSkipVerify: true, // We verify in VerifyPeerCertificate
    }

使用x509.ParseCertificate.

ParseCertificate parses a single certificate from the given ASN.1 DER data.

示例:cert, err := x509.ParseCertificate(rawCerts[0])

顺便说一句,这也是调用 VerifyPeerCertificate 的函数的方式:

// crypto/tls/handshake/handshake_client.go

func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
    for i, asn1Data := range certificates {
        cert, err := x509.ParseCertificate(asn1Data)
        if err != nil {
            c.sendAlert(alertBadCertificate)
            return errors.New("tls: failed to parse certificate from server: " + err.Error())
        }
        certs[i] = cert
    }

    // later on...
    
    if c.config.VerifyPeerCertificate != nil {
        if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
            c.sendAlert(alertBadCertificate)
            return err
        }
    }