Golang gRPC笔记02 TLS 证书认证

作者: adm 分类: go 发布时间: 2024-09-29

一、 证书生成
生成私钥

openssl genrsa -out server.key 2048

或者:

openssl ecparam -genkey -name secp384r1 -out server.key
openssl genrsa:生成RSA私钥,命令的最后一个参数,将指定生成密钥的位数,如果没有指定,默认512
openssl ecparam:生成ECC私钥,命令为椭圆曲线密钥参数生成及操作,本文中ECC曲线选择的是secp384r1

生成自签公钥

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650

填写信息:

Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:shanghai
Locality Name (eg, city) [Default City]:shanghai
Organization Name (eg, company) [Default Company Ltd]:hellokitty
Organizational Unit Name (eg, section) []:hellokitty
Common Name (eg, your name or your server's hostname) []:localhost
Email Address []:

二、 完成项目编码
项目目录:

grpc_demo/
|—— demo02/
    |—— client/
        |—— client.go   // 客户端
    |—— conf/
        |—— keys/
            |—— server.key  // 私钥
            |—— server.pem  // 公钥
    |—— proto/
        |—— prod/
            |—— prod.proto
            |—— prod.pb.go
    |—— server/
        |—— server.go   // 服务端

.proto文件:prod.proto
无变动
server端:server.go

package main

import (
	"context"
	prodpb "grpc_demo/demo02/proto/prod"
	"log"
	"net"

	"google.golang.org/grpc"

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

type ProdService struct{}

func (p ProdService) GetProdStock(_ context.Context, prodReq *prodpb.ProdRequest) (*prodpb.ProdResponse, error) {
	log.Printf("请求参数:ProdRequest.ProdId=%d", prodReq.ProdId)
	return &prodpb.ProdResponse{ProdStock: 2008}, nil
}

// 生成ProdService
func CreateProdService() ProdService {
	return ProdService{}
}

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:8899"
)

func main() {
	// 从输入证书文件和密钥文件为服务端构造TLS凭证
	// 注意在goland中引入文件以项目根目录为相对路径, 命令行直接运行需正确填写文件路径
	creds, err := credentials.NewServerTLSFromFile("demo02/conf/keys/server.pem", "demo02/conf/keys/server.key")
	if err != nil {
		log.Fatalf("credentials.NewServerTLSFromFile err: %v", err)
	}

	// 1. 创建 gRPC Server 的实例对象,并开启TLS认证
	rpcServer := grpc.NewServer(grpc.Creds(creds))
	// 2.gRPC Server 内部服务和路由的注册
	prodpb.RegisterProdServiceServer(rpcServer, CreateProdService())

	// 3. 监听指定 TCP 端口,用于接受客户端请求
	listener, err := net.Listen("tcp", Address)
	if err != nil {
		panic("net.Listen err: " + err.Error())
	}

	log.Printf("Listening on %s\n", Address)
	// 4. Serve() 调用服务器以执行阻塞等待,直到进程被终止或被 Stop() 调用
	log.Fatal(rpcServer.Serve(listener))
}

credentials.NewServerTLSFromFile:根据服务端输入的证书文件和密钥构造 TLS 凭证
grpc.Creds: 返回一个 ServerOption,用于设置服务器连接的凭据
client端: client.go

package main

import (
	"context"
	prodpb "grpc_demo/demo02/proto/prod"
	"log"

	"google.golang.org/grpc/credentials"

	"google.golang.org/grpc"
)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:8899"
)

func main() {
	// 从输入的证书公钥文件 和 自签公钥时填写的域名参数(本次签名使用的是localhost) 为客户端构造TLS凭证
	// 注意在goland中引入文件以项目根目录为相对路径, 命令行直接运行需正确填写文件路径
	creds, err := credentials.NewClientTLSFromFile("demo02/conf/keys/server.pem", "localhost")
	if err != nil {
		log.Fatalf("credentials.NewClientTLSFromFile err: %v", err)
	}

	// 连接 rpc 服务器
	conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
	if err != nil {
		panic("grpc.Dial err: " + err.Error())
	}
	defer conn.Close()

	// 初始化客户端
	client := prodpb.NewProdServiceClient(conn)
	resp, err := client.GetProdStock(context.Background(), &prodpb.ProdRequest{ProdId: 2222})
	if err != nil {
		log.Print("调用失败,err=", err)
		return
	}
	log.Printf("%+v \n", resp)
}

credentials.NewClientTLSFromFile: 从输入的证书文件中为客户端构造TLS凭证,serverNameOverride 为自签公钥时填写的域名参数
grpc.WithTransportCredentials: 配置连接级别的安全凭证(例如,TLS/SSL),返回一个DialOption,用于 grpc.Dial(target string, opts …DialOption) 设置连接选项
验证:
启动 Server:

cd demo02/server
go run server.go

Listening on 127.0.0.1:8899

启动 Client:

cd demo02/client
go run client.go

prod_stock:2008 

成功获取数据, 完成 TLS 安全认证

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!