【问题标题】:Signing certificate request with certificate authority使用证书颁发机构签署证书请求
【发布时间】:2017-03-07 07:58:35
【问题描述】:

我想使用 TLS 相互身份验证在 go 中创建的 API 上对客户端进行身份验证。我创建了一个证书颁发机构,假设 Bob 有一个他想与客户端一起使用的密钥对。 Bob 创建了一个证书请求,并希望我验证他的证书以便获得授权和 在 API 上进行身份验证。

我用它来创建我的证书颁发机构:

openssl genrsa -aes256 -out ca.key 4096
openssl req -new -x509 -sha256 -days 730 -key ca.key -out ca.crt

Bob 用它来创建他的证书和证书请求:

openssl genrsa -out bob.key 4096
openssl req -new -key bob.key -out bob.csr

我想实现这一点,但在进行中:

openssl x509 -req -days 365 -sha256 -in bob.csr -CA ca.crt -CAkey ca.key -set_serial 3 -out bob.crt

现在,通过这些命令,Bob 可以创建一个到我的 API 的 TLS 连接,它使用这个 tls.Config :

func createTLSConfig(certFile string, keyFile string, clientCAFilepath string) (config *tls.Config, err error) {
    cer, err := tls.LoadX509KeyPair(certFile, keyFile)
    if err != nil {
        return nil, err
    }

    clientCAFile, err := ioutil.ReadFile(clientCAFilepath)
    if err != nil {
        return nil, err
    }
    clientCAPool := x509.NewCertPool()
    clientCAPool.AppendCertsFromPEM(clientCAFile)

    config = &tls.Config{
        Certificates: []tls.Certificate{cer},
        ClientAuth: tls.RequireAndVerifyClientCert,
        ClientCAs:  clientCAPool,
        CipherSuites: []uint16{
            tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        },
        PreferServerCipherSuites: true,
        SessionTicketsDisabled:   false,
        MinVersion:               tls.VersionTLS12,
        CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384},
    }

    return config, nil
}

但是如果 Julia 现在想登录呢?她必须创建一个 CSR,将其发送给我,我也必须手动将她的 CSR 验证到 CRT。为了避免这种手动操作,我们的想法是有一个注册端点,Julia 可以在其中提交她的 CSR 并取回有效的 CRT。端点基本上看起来像这样:

func Register(c echo.Context) (err error) {
    // get Julia's csr from POST body
    csr := certificateFromBody(c.Body)

    // valid csr with ca to generate the crt
    crt := signCSR(csr, config.ClientCAPath)

    // return the crt to julia
    return c.JSON(http.StatusCreated, base64.StdEncoding.EncodeToString(crt))
}

我花了一些时间了解 openssl 如何使用 CA 从 CRS 创建 CRT,但没有成功。

Golang 有一个来自crypto/x509 包的CertificateRequest object,我可以使用ParseCertificateRequest 创建它,但我找不到接受这个对象和我的CA 并返回证书的函数。

感谢您的帮助!

【问题讨论】:

    标签: go x509certificate tls1.2 pki mutual-authentication


    【解决方案1】:

    现在可以了,这是一个基本的解决方案,可以通过 CA 验证来自 CRT 的 CSR:

    • 加载 ca 证书
    • 加载 ca 私钥(带密码)
    • 加载鲍勃 CSR
    • 使用 CSR 和 CA 信息创建证书模板
    • 从模板和 CA 私钥生成证书
    • 保存 bob 的证书

    一个工作示例:

    package main
    
    import (
        "crypto/rand"
        "crypto/x509"
        "encoding/pem"
        "io/ioutil"
        "math/big"
        "os"
        "time"
    )
    
    func crsToCrtExample() {
        // load CA key pair
        //      public key
        caPublicKeyFile, err := ioutil.ReadFile("certs/ca-root.crt")
        if err != nil {
            panic(err)
        }
        pemBlock, _ := pem.Decode(caPublicKeyFile)
        if pemBlock == nil {
            panic("pem.Decode failed")
        }
        caCRT, err := x509.ParseCertificate(pemBlock.Bytes)
        if err != nil {
            panic(err)
        }
    
        //      private key
        caPrivateKeyFile, err := ioutil.ReadFile("certs/ca-mutu.key")
        if err != nil {
            panic(err)
        }
        pemBlock, _ = pem.Decode(caPrivateKeyFile)
        if pemBlock == nil {
            panic("pem.Decode failed")
        }
        der, err := x509.DecryptPEMBlock(pemBlock, []byte("ca private key password"))
        if err != nil {
            panic(err)
        }
        caPrivateKey, err := x509.ParsePKCS1PrivateKey(der)
        if err != nil {
            panic(err)
        }
    
        // load client certificate request
        clientCSRFile, err := ioutil.ReadFile("certs/bob.csr")
        if err != nil {
            panic(err)
        }
        pemBlock, _ = pem.Decode(clientCSRFile)
        if pemBlock == nil {
            panic("pem.Decode failed")
        }
        clientCSR, err := x509.ParseCertificateRequest(pemBlock.Bytes)
        if err != nil {
            panic(err)
        }
        if err = clientCSR.CheckSignature(); err != nil {
            panic(err)
        }
    
        // create client certificate template
        clientCRTTemplate := x509.Certificate{
            Signature:          clientCSR.Signature,
            SignatureAlgorithm: clientCSR.SignatureAlgorithm,
    
            PublicKeyAlgorithm: clientCSR.PublicKeyAlgorithm,
            PublicKey:          clientCSR.PublicKey,
    
            SerialNumber: big.NewInt(2),
            Issuer:       caCRT.Subject,
            Subject:      clientCSR.Subject,
            NotBefore:    time.Now(),
            NotAfter:     time.Now().Add(24 * time.Hour),
            KeyUsage:     x509.KeyUsageDigitalSignature,
            ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
        }
    
        // create client certificate from template and CA public key
        clientCRTRaw, err := x509.CreateCertificate(rand.Reader, &clientCRTTemplate, caCRT, clientCSR.PublicKey, caPrivateKey)
        if err != nil {
            panic(err)
        }
    
        // save the certificate
        clientCRTFile, err := os.Create("certs/bob.crt")
        if err != nil {
            panic(err)
        }
        pem.Encode(clientCRTFile, &pem.Block{Type: "CERTIFICATE", Bytes: clientCRTRaw})
        clientCRTFile.Close()
    }
    

    谢谢马克!

    【讨论】:

      【解决方案2】:

      您也许可以使用x509.CreateCertificate

      CreateCertificate 的参数之一是“模板”证书。

      您可以使用 Julia 的 CertificateRequest 中的字段设置模板证书的字段。

      Go 的 generate cert 脚本显示了 CreateCertificate 的示例用法。

      这假设来自 Julia 的 API 请求确实来自 Julia,并且被充分信任以签署请求并返回证书。

      另外,Using your own PKI for TLS in Go 可能会有所帮助。

      【讨论】:

        【解决方案3】:

        这是我为一篇关于 PKI 的博客文章编写的演示程序的代码片段。全文:https://anchorloop.com/2017/09/25/security-iq-ii-public-key-infrastructure/

        // Now read that number of bytes and parse the certificate request
        asn1Data := make([]byte, asn1DataSize)
        _, err = reader.Read(asn1Data)
        if err != nil {
            return err
        }
        fmt.Println("Received Certificate Signing Request.")
        certReq, err := x509.ParseCertificateRequest(asn1Data)
        if err != nil {
            return err
        }
        
        // Create template for certificate creation, uses properties from the request and root certificate.
        serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
        serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
        if err != nil {
            return err
        }
        template := x509.Certificate {
            Signature: certReq.Signature,
            SignatureAlgorithm: certReq.SignatureAlgorithm,
        
            PublicKeyAlgorithm: certReq.PublicKeyAlgorithm,
            PublicKey: certReq.PublicKey,
        
            SerialNumber: serialNumber,
            Issuer: rootCert.Subject,
            Subject: certReq.Subject,
            NotBefore: time.Now(),
            NotAfter: time.Now().Add(time.Hour * 24 * 365),
            KeyUsage: x509.KeyUsageDigitalSignature,
            ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
        }
        
        // Create certificate from template and root certificate, signed by the RootCA's private key.
        certData, err := x509.CreateCertificate(rand.Reader, &template, rootCert, template.PublicKey, privateKey)
        if err != nil {
            return err
        }
        fmt.Println("Created Certificate from CSR, signed by RootCA's Private Key.")
        

        基本上:

        • CSR 由客户端创建和发送。
        • 签名证书的所有者对其进行解析并构建一个新的 x509.Certificate,该证书来自 CSR 和签名证书的混合属性。
        • 签名者的私钥被传递给 x509.CreateCertificate 进行签名。
        • 之后,您可以将其发送回客户端。

        希望对你有帮助。

        【讨论】:

        猜你喜欢
        • 2012-04-18
        • 2014-02-13
        • 2018-10-08
        • 1970-01-01
        • 2021-08-22
        • 2011-09-24
        相关资源
        最近更新 更多