Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Signing certificate request with certificate authority

I want to use TLS mutual authentication to authenticate a client on a API made in go. I've created a certificate authority, and let's say Bob has a key pair he wants to use with the client. Bob created a certificate request and want me to validate his certificate in order to be authorized and authenticated on the API.

I've used this to create my Certificate Authority :

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

Bob used this to create his certificate and certificate request :

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

I want to achive this, but in go :

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

For now, with theses commands, Bob can create a TLS connection to my API which use this 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
}

But what if Julia now want to login ? She will have to create a CSR, send it to me and I would have to manually validate her CSR to a CRT too. To avoid this manual operation, the idea is to have a register endpoint where Julia can submit her CSR and get back a valid CRT. The endpoint will basically look like this :

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

I spend some time to understand how openssl use the CA to create the CRT from the CRS, without success.

Golang has a CertificateRequest object from the crypto/x509 package that I can create with the ParseCertificateRequest but I can't find the function that take this object and my CA and return a certificate.

Thank you for your help!

like image 827
krostar Avatar asked Mar 07 '17 07:03

krostar


People also ask

What is certificate signing authority?

A certificate authority (CA) is a trusted entity that issues Secure Sockets Layer (SSL) certificates. These digital certificates are data files used to cryptographically link an entity with a public key. Web browsers use them to authenticate content sent from web servers, ensuring trust in content delivered online.

How do I send a certificate signing request?

Go to Start > Administrative Tools > Internet Information Servicess (IIS) Manager. Select the server name from the left-side panel. In the center panel, double-click Server Certificates. In the Actions menu from the right-side, click Create Certificate Request.


2 Answers

It work now, here is a basic solution to validate a CSR from a CRT with a CA:

  • load ca certificate
  • load ca private key (with password)
  • load bob CSR
  • create a certificate template with the CSR and CA informations
  • generate the certificate from the template and with the CA private key
  • save the bob's certificate

A working example:

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()
}

Thanks Mark!

like image 133
krostar Avatar answered Oct 03 '22 07:10

krostar


You may be able to use x509.CreateCertificate.

One of the parameters to CreateCertificate is a 'template' certificate.

You can set the fields of the template certificate using the fields from Julia's CertificateRequest.

Go's generate cert script shows an example usage CreateCertificate.

This assumes the API request from Julia is really from Julia, and sufficiently trusted to sign the request and return a certificate.

Also, Using your own PKI for TLS in Go may be of help.

like image 39
Mark Avatar answered Oct 03 '22 06:10

Mark



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!