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

Go:使用存储在智能卡(Windows)上的客户端证书的HTTPS请求,go,ssl,smartcard,client-certificates,mutual-authentication,Go,Ssl,Smartcard,Client Certificates,Mutual Authentication,要执行客户端证书身份验证(相互身份验证),我发现的所有示例都假定可以访问私钥(例如,从文件)。生成包含私钥和公钥的证书,如下所示: cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key") 现在,我必须从智能卡上获取证书(和私钥,据我所知,私钥无法提取-签名应该通过PKCS#11完成)。到目前为止,我能够从Windows证书存储中枚举证书: store, err := syscall.UTF16PtrFro

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

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)
            }
        }
    }
}
我想失败是意料之中的,因为我没有提供私钥

Java(SunMSCAPI)和.NET似乎在暗中使用智能卡上的私钥,例如,我的做法与上面差不多,身份验证“正常工作”


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

您为
tls.Certificate
指定的私钥可以是实现
crypto.Signer
的任何对象,根据文档:

是可用于签名操作的不透明私钥的接口。例如,保存在硬件模块中的RSA密钥

正是为了这种用途


一旦访问了底层密钥,实现接口就相当简单了。例如,为PKCS#11键提供了这样的实现

您可以使用供应商的PKCS11文件+库

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,
}
package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "github.com/ThalesIgnite/crypto11"
)

func main() {
    config := crypto11.Config{
        Path: "C:\\Windows\\System32\\vendor-pkcs11.dll",
        TokenSerial: "123456789456123",
        Pin: "123456",
    }

    context, err := crypto11.Configure(&config)
    if err != nil{
        log.Fatalln(err)
    }

    certificates, err := context.FindAllPairedCertificates()
    if err != nil{
        log.Fatalln(err)
    }

    fmt.Println("total certificates: ", len(certificates))

    cert := certificates[0]
    client := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                Certificates: []tls.Certificate{cert},
                Renegotiation:      tls.RenegotiateOnceAsClient,
            },
        },
    }

    req, err := http.NewRequest("GET", "https://server.cryptomix.com:443/secure/", nil)
    if err != nil {
        log.Fatalln(err)
    }

    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")

    resp, err := client.Do(req)
    if err != nil {
        log.Fatalln(err)
    }

    fmt.Println("status code: ", resp.StatusCode)

    if resp.StatusCode == http.StatusOK {
        bodyBytes, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Fatal(err)
        }
        bodyString := string(bodyBytes)
        fmt.Println(bodyString)
    }
}