Golang gRPC笔记02 TLS 证书认证
一、 证书生成
生成私钥
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 安全认证

