前言
假设gRPC服务端的主机名为qw.er.com
,需要为gRPC服务端和客户端之间的通信配置tls双向认证加密。
生成证书
- 生成ca根证书。生成过程会要求填写密码、CN、ON、OU等信息,记住密码。
openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt -subj "/CN=qw.er.com" -days 365
- 新建并编辑文件
openssl.cnf
文件。req_distinguished_name中内容按需填写,DNS.1要替换成实际域名。
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
countryName = CN
stateOrProvinceName = Anhui
localityName = Hefei
organizationName = zhangsan
commonName = qw.er.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = qw.er.com
- 生成服务端证书
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=qw.er.com" -config openssl.cnf
# 提示输入ca私钥的密码
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf
- 生成客户端证书
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=qw.er.com" -config openssl.cnf
# 提示输入ca私钥的密码
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf
proto示例
用的还是入门级的helloworld
syntax = "proto3"; // protocol buffers版本
option go_package = "./;proto"; // 生成的Go代码将被放在当前目录,并使用proto作为包名称
// 定义grpc服务的接口。服务就是一组可被远程调用的方法
service Greeter {
// 定义远程调用方法
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 定义消息格式和消息类型
message HelloRequest {
string name = 1; // 1 是二进制格式中的字段编号, 应该唯一
}
message HelloReply {
string message = 1;
}
生成go代码:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto
服务端代码示例
如果需要客户端和服务端直接通信,可以参考以下示例代码。
package main
import (
pb "grpcs/proto"
"context"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"log"
"net"
"os"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var (
port = flag.Int("port", 8010, "the server port")
crtFile = flag.String("crt", "server.crt", "the server crt file")
keyFile = flag.String("key", "server.key", "the server key file")
caFile = flag.String("ca", "ca.crt", "the server ca file")
)
type server struct{
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
// 通过服务端的证书和密钥直接创建X.509密钥对
certificate, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
if err != nil {
log.Fatalf("Failed to load key pair: %v", err)
}
// 通过CA创建证书池
certPool := x509.NewCertPool()
ca, err := os.ReadFile(*caFile)
if err != nil {
log.Fatalf("Failed to read ca: %v", err)
}
// 将来自CA的客户端证书附加到证书池
if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("Failed to append ca certificate")
}
opts := []grpc.ServerOption{
grpc.Creds( // 为所有传入的连接启用TLS
credentials.NewTLS(&tls.Config