preface

Hello, I am fried fish. In the last chapter, we asked a question. How to ensure the reliability and validity of certificates? How do you make sure your Server and Client certificates are correct?

CA

To ensure the reliability and validity of certificates, you can introduce the concept of the root certificate issued by the CA. It complies with the X.509 standard

Root certificate

A root certificate is a public key certificate belonging to the root Certification Authority (CA). We can trust CA by verifying its signature. Anyone can obtain CA’s certificate (including public key) to verify the certificate issued by CA (client, server).

It contains the following files:

  • The public key
  • The key

To generate the Key

openssl genrsa -out ca.key 2048
Copy the code

Generate the key

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

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 []:
Copy the code

Server

To generate a CSR

openssl req -new -key server.key -out server.csr
Copy the code
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 []:

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

CSR is an acronym for Cerificate Signing Request. The main effect is that THE CA will use the CSR file to sign so that the attacker cannot disguise or tamper with the original certificate

CA based Issue

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

Client

To generate the Key

openssl ecparam -genkey -name secp384r1 -out client.key
Copy the code

To generate a CSR

openssl req -new -key client.key -out client.csr
Copy the code

CA based Issue

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

Finishing directory

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

└── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ─ Server ├ ─ ─ for server CSR ├ ─ ─ for server key └ ─ ─ for server pemCopy the code

In addition, some documents should not appear in the warehouse, should be confidential or deleted. But I’m saving it for the sake of the demonstration. (Knocks on the blackboard)

gRPC

Next, you’ll start coding for gRPC in earnest, transforming the code from the previous chapter. The goal is CA based TLS authentication 🤫

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")
	iferr ! = nil { log.Fatalf("tls.LoadX509KeyPair err: %v", err)
	}

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

	ifok := 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)
	iferr ! = nil { log.Fatalf("net.Listen err: %v", err)
	}

	server.Serve(lis)
}
Copy the code
  • Tls.loadx509keypair () : reads and parses information from the certificate file to obtain the certificate public key and key pair
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	iferr ! = nil {return Certificate{}, err
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	iferr ! = nil {return Certificate{}, err
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}
Copy the code
  • X509.newcertpool () : Creates a new, empty CertPool
  • CertPool. AppendCertsFromPEM () : try to parse the incoming PEM encoded certificate. If the parse succeeds, it will be added to CertPool for later use
  • NewTLS: Builds the TransportCredentials option based on TLS
  • Tls. Config: The Config structure is used to configure the TLS client or server

On the Server, three Config configuration items are used:

(1) Certificates: Set up the chain of Certificates, allowing one or more Certificates

(2) ClientAuth: the client certificate must be verified. You can select the following parameters as required:

const (
	NoClientCert ClientAuthType = iota
	RequestClientCert
	RequireAnyClientCert
	VerifyClientCertIfGiven
	RequireAndVerifyClientCert
)
Copy the code

(3) ClientCAs: Sets the set of root certificates. The verification mode is 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")
	iferr ! = nil { log.Fatalf("tls.LoadX509KeyPair err: %v", err)
	}

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

	ifok := 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))
	iferr ! = 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",})iferr ! = nil { log.Fatalf("client.Search err: %v", err)
	}

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

In most cases, the Client is the same as the Server. The difference is that the Client uses the root certificate and ServerName to verify the Server when the Client requests the Server

The simple process is as follows:

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

. Of course, in setting up the TLS RequireAndVerifyClientCert mode, the Server will use the root certificate of the CA certification on the Client side certificates for reliability and validity check. That is, both sides will be checked, which greatly ensures the safety 👍

validation

Restart server.go and run client.go to check whether the response is normal

conclusion

In this section, we use the root certificate issued by the CA to sign the client and server certificates. Further improve the communication security of the two

This time it’s really done!

?

If you have any questions or mistakes, welcome to raise questions or give correction opinions on issues. If you like or are helpful to you, welcome Star, which is a kind of encouragement and promotion for the author.

My official account

reference

Sample code for this series

  • go-grpc-example