Bring in gRPC: ca-based TLS certificate authentication

  golang, grpc, php, tls

Bring in gRPC: ca-based TLS certificate authentication

Original address:Bring in gRPC: ca-based TLS certificate authentication
Project address:https://github.com/EDDYCJY/go …

Preface

In the previous chapter, we raised a question. How to ensure the reliability and validity of the certificate? How do you make sure your Server and Client certificates are correct?

CA

In order to ensure the reliability and validity of the certificate, the concept of root certificate issued by CA can be introduced here. It complies with the X.509 standard.

Root certificate

The root certificate is a public key certificate belonging to the root certification authority (CA). We can trust the CA by verifying the CA’s signature. Anyone can obtain the CA’s certificate (including the public key) to verify the certificate it issues (client and server)

It contains the following documents:

  • Public key
  • Key

Generate Key

openssl genrsa -out ca.key 2048

Generate key

openssl req -new -x509 -days 7200 -key ca.key -out ca.pem

Fill in the information

Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:go-grpc-example
Email Address []:

Server

Generate CSR

openssl req -new -key server.key -out server.csr
填写信息
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:go-grpc-example
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

CSR is the English abbreviation of CertificateSigning Request, which is a certificate request document. The main function is that CA will use CSR files to sign so that attackers cannot disguise or tamper with the original certificate.

Ca-based issuance

openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in server.csr -out server.pem

Client

Generate Key

openssl ecparam -genkey -name secp384r1 -out client.key

Generate CSR

openssl req -new -key client.key -out client.csr

Ca-based issuance

openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in client.csr -out client.pem

Organize catalogues

So far we have generated a pile of files, please store them in the following directory structure:

$ tree conf 
conf
├── ca.key
├── ca.pem
├── ca.srl
├── client
│   ├── client.csr
│   ├── client.key
│   └── client.pem
└── server
    ├── server.csr
    ├── server.key
    └── server.pem

Other documents should not appear in the warehouse and should be kept confidential or deleted. But for the sake of real demonstration, I kept it (knocking on the blackboard)

gRPC

Next, the gRPC will be formally coded and the code in the previous chapter will be modified. The goal is TLS authentication based on CA.

Server

package main

import (
    "context"
    "log"
    "net"
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"

    pb "github.com/EDDYCJY/go-grpc-example/proto"
)

...

const PORT = "9001"

func main() {
    cert, err := tls.LoadX509KeyPair("../../conf/server/server.pem", "../../conf/server/server.key")
    if err != nil {
        log.Fatalf("tls.LoadX509KeyPair err: %v", err)
    }

    certPool := x509.NewCertPool()
    ca, err := ioutil.ReadFile("../../conf/ca.pem")
    if err != nil {
        log.Fatalf("ioutil.ReadFile err: %v", err)
    }

    if ok := certPool.AppendCertsFromPEM(ca); !ok {
        log.Fatalf("certPool.AppendCertsFromPEM err")
    }

    c := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ClientAuth:   tls.RequireAndVerifyClientCert,
        ClientCAs:    certPool,
    })

    server := grpc.NewServer(grpc.Creds(c))
    pb.RegisterSearchServiceServer(server, &SearchService{})

    lis, err := net.Listen("tcp", ":"+PORT)
    if err != nil {
        log.Fatalf("net.Listen err: %v", err)
    }

    server.Serve(lis)
}
  • Tls.LoadX509KeyPair (): from certificate related filesreadAndanalysisInformation, get the certificate public key, key pair
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
    certPEMBlock, err := ioutil.ReadFile(certFile)
    if err != nil {
        return Certificate{}, err
    }
    keyPEMBlock, err := ioutil.ReadFile(keyFile)
    if err != nil {
        return Certificate{}, err
    }
    return X509KeyPair(certPEMBlock, keyPEMBlock)
}
  • X509.NewCertPool (): create a new, empty CertPool
  • CertPool.AppendCertsFromPEM (): attempts to resolve the passed PEM-encoded certificate. If the resolution is successful, it will be added to CertPool for later use.
  • Newtls: build tls-based TransportCredentials option
  • Tls.Config:Config structure is used to configure TLS clients or servers

On the Server, a total of three Config configuration items are used:

(1)Certificates: Set up a certificate chain that can contain one or more

(2)ClientAuth: the client’s certificate must be verified. The following parameters can be selected according to the actual situation:

const (
    NoClientCert ClientAuthType = iota
    RequestClientCert
    RequireAnyClientCert
    VerifyClientCertIfGiven
    RequireAndVerifyClientCert
)

(3)ClientCAs: sets the set of root certificates, and the verification method uses the mode set in ClientAuth.

Client

package main

import (
    "context"
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "log"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"

    pb "github.com/EDDYCJY/go-grpc-example/proto"
)

const PORT = "9001"

func main() {
    cert, err := tls.LoadX509KeyPair("../../conf/client/client.pem", "../../conf/client/client.key")
    if err != nil {
        log.Fatalf("tls.LoadX509KeyPair err: %v", err)
    }

    certPool := x509.NewCertPool()
    ca, err := ioutil.ReadFile("../../conf/ca.pem")
    if err != nil {
        log.Fatalf("ioutil.ReadFile err: %v", err)
    }

    if ok := certPool.AppendCertsFromPEM(ca); !ok {
        log.Fatalf("certPool.AppendCertsFromPEM err")
    }

    c := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ServerName:   "go-grpc-example",
        RootCAs:      certPool,
    })

    conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c))
    if err != nil {
        log.Fatalf("grpc.Dial err: %v", err)
    }
    defer conn.Close()

    client := pb.NewSearchServiceClient(conn)
    resp, err := client.Search(context.Background(), &pb.SearchRequest{
        Request: "gRPC",
    })
    if err != nil {
        log.Fatalf("client.Search err: %v", err)
    }

    log.Printf("resp: %s", resp.GetResponse())
}

Most of the Clients are consistent with the Server. the difference is that when the Client requests the Server, the client uses the root certificate and ServerName to check the Server.

The simple process is roughly as follows:

  1. Client obtains server-side certificate through request
  2. The root certificate authenticated by CA is used to verify the reliability and validity of the certificate on the Server side.
  3. Verify that ServerName is available and valid

Of course, in setting uptls.RequireAndVerifyClientCertIn CAse of mode, Server will also use the root certificate authenticated by ca to verify the reliability and validity of the client-side certificate. In other words, both sides will check, thus greatly ensuring safety.

Verification

Restart server.go and execute client.go to see if the response results are normal.

Summary

In this chapter, we use the root certificate issued by CA to issue the certificates of client and server. The communication safety between the two is further improved

This time is really accomplished!

References

This series of sample codes

Series catalog