猿问

Go:使用存储在 SmartCard 上的客户端证书的 HTTPS 请求 (Windows)

为了执行客户端证书身份验证(相互身份验证),我发现的所有示例都假设可以访问私钥(例如从文件中访问)。生成包含私钥和公钥的证书,如下所示:


cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")

现在,我必须从智能卡中获取证书(和私钥,据我所知无法提取 - 签名应该通过 PKCS#11 完成)。到目前为止,我能够枚举 Windows 证书存储中的证书:


store, err := syscall.UTF16PtrFromString("MY")

storeHandle, err := syscall.CertOpenSystemStore(0, store)

if err != nil {

    fmt.Println(syscall.GetLastError())

}


var certs []*x509.Certificate

var cert *syscall.CertContext

for {

    cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert)

    if err != nil {

        if errno, ok := err.(syscall.Errno); ok {

            if errno == CRYPT_E_NOT_FOUND {

                break

            }

        }

        fmt.Println(syscall.GetLastError())

    }

    if cert == nil {

        break

    }

    // Copy the buf, since ParseCertificate does not create its own copy.

    buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]

    buf2 := make([]byte, cert.Length)

    copy(buf2, buf)

    if c, err := x509.ParseCertificate(buf2); err == nil {

        for _, value := range c.ExtKeyUsage {

            if value == x509.ExtKeyUsageClientAuth {

                fmt.Println(c.Subject.CommonName)

                fmt.Println(c.Issuer.CommonName)

                certs = append(certs, c)

            }

        }

    }

}

这种方式检索到的证书确实来自智能卡。稍后使用时,身份验证失败:


cer:= tls.Certificate{Certificate: [][]byte{certs[0].Raw}, Leaf: certs[0],}


tlsConfig := &tls.Config{

    Certificates:       []tls.Certificate{cer},

    RootCAs:            caCertPool,

    InsecureSkipVerify: true,

}


transport := &http.Transport{TLSClientConfig: tlsConfig}


client := http.Client{

    Timeout:   time.Minute * 2,

    Transport: transport,

}

我想失败是意料之中的,因为我没有提供私钥。


Java (SunMSCAPI) 和 .NET 似乎在幕后使用智能卡上的私钥,例如,我做的与上面几乎相同,并且身份验证“正常工作”。


有没有办法用 Go 实现这一点?


aluckdog
浏览 156回答 2
2回答

凤凰求蛊

您为您指定的私钥tls.Certificate可以是任何实现的对象crypto.Signer,根据文档:是可用于签名操作的不透明私钥的接口。例如,保存在硬件模块中的 RSA 密钥。并且专门用于这种用途。一旦您可以访问底层密钥,实现接口就相当简单了。例如, thalesignite/crypto11为 PKCS#11 密钥提供了这样的实现。

千万里不及你

您可以使用供应商的 PKCS11 文件 + crypto11库。package mainimport (&nbsp; &nbsp; "crypto/tls"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "io/ioutil"&nbsp; &nbsp; "log"&nbsp; &nbsp; "net/http"&nbsp; &nbsp; "github.com/ThalesIgnite/crypto11")func main() {&nbsp; &nbsp; config := crypto11.Config{&nbsp; &nbsp; &nbsp; &nbsp; Path: "C:\\Windows\\System32\\vendor-pkcs11.dll",&nbsp; &nbsp; &nbsp; &nbsp; TokenSerial: "123456789456123",&nbsp; &nbsp; &nbsp; &nbsp; Pin: "123456",&nbsp; &nbsp; }&nbsp; &nbsp; context, err := crypto11.Configure(&config)&nbsp; &nbsp; if err != nil{&nbsp; &nbsp; &nbsp; &nbsp; log.Fatalln(err)&nbsp; &nbsp; }&nbsp; &nbsp; certificates, err := context.FindAllPairedCertificates()&nbsp; &nbsp; if err != nil{&nbsp; &nbsp; &nbsp; &nbsp; log.Fatalln(err)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("total certificates: ", len(certificates))&nbsp; &nbsp; cert := certificates[0]&nbsp; &nbsp; client := &http.Client{&nbsp; &nbsp; &nbsp; &nbsp; Transport: &http.Transport{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TLSClientConfig: &tls.Config{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Certificates: []tls.Certificate{cert},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Renegotiation:&nbsp; &nbsp; &nbsp; tls.RenegotiateOnceAsClient,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; }&nbsp; &nbsp; req, err := http.NewRequest("GET", "https://server.cryptomix.com:443/secure/", nil)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatalln(err)&nbsp; &nbsp; }&nbsp; &nbsp; req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36")&nbsp; &nbsp; resp, err := client.Do(req)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatalln(err)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("status code: ", resp.StatusCode)&nbsp; &nbsp; if resp.StatusCode == http.StatusOK {&nbsp; &nbsp; &nbsp; &nbsp; bodyBytes, err := ioutil.ReadAll(resp.Body)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; bodyString := string(bodyBytes)&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(bodyString)&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Go
我要回答