GRPC单向安全连接
从本篇开始,我将介绍加强 gRPC 的安全性的一系列措施。本篇介绍使用 TLS 加密 gRPC 通信的第一篇文章: gRPC 单向安全连接。 TLS 协议介绍 传输层安全性协议(英语:Transport Layer Security,缩写作TLS),及其前身安全套接层(Secure Sockets Layer,缩写作SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。网景公司(Netscape)在1994年推出首版网页浏览器,网景导航者时,推出HTTPS协议,以SSL进行加密,这是SSL的起源。IETF将SSL进行标准化,1999年公布第一版TLS标准文件。随后又公布RFC 5246 (2008年8月)与RFC 6176(2011年3月)。在浏览器、邮箱、即时通信、VoIP、网络传真等应用程序中,广泛支持这个协议。主要的网站,如Google、Facebook等也以这个协议来创建安全连线,发送数据。目前已成为互联网上保密通信的工业标准。 SSL包含记录层(Record Layer)和传输层,记录层协议确定传输层数据的封装格式。传输层安全协议使用X.509认证,之后利用非对称加密演算来对通信方做身份认证,之后交换对称密钥作为会谈密钥(Session key)。这个会谈密钥是用来将通信两方交换的数据做加密,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。 单向安全连接 通过安全的连接进行传输数据非常重要,那么如何在 gRPC 中使用 TLS 保护 gRPC 通信呢? TLS 认证机制集成在了 gRPC 库中,这使得 gRPC 可以很方便使用 TLS 进行安全连接。 客户端和服务端之间的安全传输可以采用单向或双向的方式来实现。本文主要介绍 单向安全连接。 在单向安全连接中,只有客户端会校验服务端,以确保它所接收的数据来自预期的服务器,在建立连接时,服务端会与客户端共享其公开证书,客户端会校验收到的证书。这是通过证书授权中心完成的。 证书校验完成后,客户端会使用密钥加密数据。 要启用 TLS ,需要证书和密钥(xx.key,xx.pem/xx.crt),前者是用于签名和扔着公钥,后者用于分发自签名 X.509 公钥。证书和密钥的生成这里就不过多介绍了,需要的可以自行了解。 在 gRPC 服务端启用单向安全连接 在 gRPC 服务端启用单向安全连接的主要流程如下: 1 读取和解析公钥-私钥,创建启用 TLS 的证书 2 添加证书作为 TLS 服务凭证,为所有连接启用 TLS. 3 通过 TLS 凭证创建新的 gRPC 连接 接下来的流程跟普通的流程差不多,就不多介绍了,直接上代码: package main import ( "context" "crypto/tls" "errors" wrapper "github.com/golang/protobuf/ptypes/wrappers" "github.com/google/uuid" "google.golang.org/grpc" "google.golang.org/grpc/credentials" pb "grpc-demo/proto" "log" "net" ) const ( port = ":50051" crtFile = "./server.crt" keyFile = "./server.key" ) type server struct { pb.UnimplementedProductInfoServer productMap map[string]*pb.Product } func (s *server) AddProduct(ctx context.Context, in *pb.Product) (*wrapper.StringValue, error) { out, err := uuid.NewUUID() if err != nil { log.Fatal(err) } in.Id = out.String() if s.productMap == nil { s.productMap = make(map[string]*pb.Product) } s.productMap[in.Id] = in return &wrapper.StringValue{Value: in.Id}, nil } func (s *server) GetProduct(ctx context.Context, in *wrapper.StringValue) (*pb.Product, error) { value, exists := s.productMap[in.Value] if exists { return value, nil } return nil, errors.New("Product does not exist for the ID" + in.Value) } func main() { cert, err := tls.LoadX509KeyPair(crtFile, keyFile) if err != nil { log.Fatalf("failed to load key pair: %s", err) } opts := []grpc.ServerOption{ grpc.Creds(credentials.NewServerTLSFromCert(&cert)), } s := grpc.NewServer(opts...) pb.RegisterProductInfoServer(s, &server{}) lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } 在客户端启用单向安全连接 为了与服务器连接,客户都需要服务端的自认证公钥。具体流程如下: ...