首页 > 其他分享 >写给go开发者的gRPC教程-通信安全

写给go开发者的gRPC教程-通信安全

时间:2023-06-05 09:23:33浏览次数:45  
标签:通信安全 err 证书 gRPC server x509 go 服务端 客户端

使用 TLS 安全传输数据

什么是 SSL/TLS

SSL 包含记录层(Record Layer)和传输层[1],记录层协议确定传输层数据的封装格式。传输层安全协议使用X.509[2]认证,之后利用非对称加密演算来对通信方做身份认证,之后交换对称密匙作为会话密匙(Session key[3])。这个会谈密匙是用来将通信两方交换的资料做加密,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。

--- 维基百科

简单点说就是:SSL/TLS 是一个安全协议,它通过一系列的手段、一系列的算法让客户端与服务端之间加密传输数据,避免数据被攻击者窃听。快速入门 TLS 可以参考:一文带你快速入门 TLS/SSL

SSL/TLS 分为单向认证和双向认证(mtls)

单向认证

在单向认证中,仅客户端验证服务端

当客户端和服务端建立连接之后,服务端会发送公开的证书给客户端,客户端验证证书后使用证书中包含的密钥信息来发送加密数据(实际要比这个复杂,这里简化了交互流程)

TLS 证书可以使用根证书创建子证书,因此对于证书有两种使用方式,一种是直接使用根证书,一种是使用由根证书签发的子证书

✨ 直接使用根证书

所以对于服务端来说需要两个文件

server.key:RSA 的私钥,用来进行数字签名

server.pem/server.crt:自签名的服务端证书,其中包含与私钥对应的公钥、网站域名、签名算法等信息

对于客户端来说不需要准备文件

图片单向认证-直接使用根证书

服务端代码如下:

1)NewServerTLSFromFile加载证书 2)NewServer 时指定 Creds

func main() {
 l, err := net.Listen("tcp", ":8009")
 if err != nil {
  panic(err)
 }

 // method 1.
 creds, err := credentials.NewServerTLSFromFile("./x509/server.crt", "./x509/server.key")
 if err != nil {
  panic(err)
 }

 // method 2.
 // cert , err := tls.LoadX509KeyPair("./x509/server.crt", "./x509/server.key")
 // if err != nil {
 //  panic(err)
 // }

 // creds := credentials.NewServerTLSFromCert(&cert)

 s := grpc.NewServer(grpc.Creds(creds))

 pb.RegisterOrderManagementServer(s, &server{})

 if err := s.Serve(l); err != nil {
  panic(err)
 }
}

客户端代码如下:

1)NewClientTLSFromFile指定使用 CA 证书来校验服务端的证书有效性。

  • 注意:第二个参数域名就是服务端证书时的 CN 参数

2)建立连接时 指定建立安全连接WithTransportCredentials

func main() {
 creds, err := credentials.NewClientTLSFromFile("./x509/server.crt", "www.example.com")
 if err != nil {
  panic(err)
 }

 conn, err := grpc.Dial("localhost:8009", grpc.WithTransportCredentials(creds))
 if err != nil {
  panic(err)
 }
 defer conn.Close()

 client := pb.NewOrderManagementClient(conn)

 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 defer cancel()

 // Get Order
 retrievedOrder, err := client.GetOrder(ctx, &wrapperspb.StringValue{Value: "101"})
 if err != nil {
  panic(err)
 }

 log.Print("GetOrder Response -> : ", retrievedOrder)
}

✨ 根证书模式

在生产环境通常是生成一个 CA 根证书,然后使用 CA 根证书去签名多个服务端的证书。这种方式可以一次管理多个证书,也比较贴近真实情况,当然也可以用来做证书过期、更新等试验,缺点是操作起来略微麻烦一点。

rootCA.key:ca 机构的私钥,用来给服务端签发证书

rootCA.crt:ca 的证书,用来给客户端验证服务端证书

server.key:RSA 的私钥,用来进行数字签名

server.pem/server.crt:由 ca 签发的服务端证书

图片单向认证-根证书模式

服务端代码如下:

1)NewServerTLSFromFile加载证书 2)NewServer 时指定 Creds

func main() {
 l, err := net.Listen("tcp", ":8009")
 if err != nil {
  panic(err)
 }

 // method 1.
 creds, err := credentials.NewServerTLSFromFile("./x509/server.crt", "./x509/server.key")
 if err != nil {
  panic(err)
 }

 // method 2.
 // cert , err := tls.LoadX509KeyPair("./x509/server.crt", "./x509/server.key")
 // if err != nil {
 //  panic(err)
 // }

 // creds := credentials.NewServerTLSFromCert(&cert)

 s := grpc.NewServer(grpc.Creds(creds))

 pb.RegisterOrderManagementServer(s, &server{})

 if err := s.Serve(l); err != nil {
  panic(err)
 }
}

客户端代码如下:

1)NewClientTLSFromFile 指定使用CA 根证书来校验服务端的证书有效性。

  • 注意:第二个参数域名就是服务端证书时的 CN 参数

2)建立连接时 指定建立安全连接WithTransportCredentials

func main() {
 creds, err := credentials.NewClientTLSFromFile("./x509/rootCa.crt", "www.example.com")
 if err != nil {
  panic(err)
 }

 conn, err := grpc.Dial("localhost:8009", grpc.WithTransportCredentials(creds))
 if err != nil {
  panic(err)
 }
 defer conn.Close()

 client := pb.NewOrderManagementClient(conn)

 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 defer cancel()

 // Get Order
 retrievedOrder, err := client.GetOrder(ctx, &wrapperspb.StringValue{Value: "101"})
 if err != nil {
  panic(err)
 }

 log.Print("GetOrder Response -> : ", retrievedOrder)
}

双向认证(mTLS)

server-side TLS 中虽然服务端使用了证书,但是客户端却没有使用证书,本章节会给客户端也生成一个证书,并完成 mutual TLS

直接使用根证书

在 mTLS 中很少会有直接使用根证书的场景,这里仅放一个交互图,不放代码了

图片双向认证-直接使用根证书

根证书模式

rootCA.key:ca 机构的私钥,用来给服务端签发证书

rootCA.crt:ca 的证书,用来给客户端验证服务端证书

server.key:服务端 RSA 的私钥,用来进行数字签名

server.pem/server.crt:由 ca 签发的服务端证书

client.key: 客户端 RSA 私钥,用来进行数字签名

client.pem/client.crt: 由 ca 签发的客户端证书

图片双向认证-根证书模式

服务端代码如下:

1)加载服务端证书

2)构建用于校验客户端证书的CertPool

3)使用上面的参数构建一个TransportCredentials

4)newServer 是指定使用前面创建的creds

看似改动很大,其实如果你仔细查看了前面NewServerTLSFromFile方法做的事的话,就会发现是差不多的,只有极个别参数不同。

修改点如下:

1)tls.Config的参数ClientAuth,这里改成了tls.RequireAndVerifyClientCert,即服务端也必须校验客户端的证书,之前使用的默认值(即不校验)

2)tls.Config的参数ClientCAs,由于之前都不校验客户端证书,所以也没有指定用什么证书来校验

func main() {
 l, err := net.Listen("tcp", ":8009")
 if err != nil {
  panic(err)
 }

 certificate, err := tls.LoadX509KeyPair("./x509/server.crt", "./x509/server.key")
 if err != nil {
  panic(err)
 }

 // 创建CertPool,后续就用池里的证书来校验客户端证书有效性
 // 所以如果有多个客户端 可以给每个客户端使用不同的 CA 证书,来实现分别校验的目的
 certPool := x509.NewCertPool()
 ca, err := ioutil.ReadFile("./x509/rootCa.crt")
 if err != nil {
  panic(err)
 }
 if ok := certPool.AppendCertsFromPEM(ca); !ok {
  log.Fatal("failed to append certs")
 }

 // 构建基于 TLS 的 TransportCredentials
 creds := credentials.NewTLS(&tls.Config{
  // 设置证书链,允许包含一个或多个
  Certificates: []tls.Certificate{certificate},
  // 要求必须校验客户端的证书 可以根据实际情况选用其他参数
  ClientAuth: tls.RequireAndVerifyClientCert, // NOTE: this is optional!
  // 设置根证书的集合,校验方式使用 ClientAuth 中设定的模式
  ClientCAs: certPool,
 })

 s := grpc.NewServer(grpc.Creds(creds))

 pb.RegisterOrderManagementServer(s, &server{})

 if err := s.Serve(l); err != nil {
  panic(err)
 }
}

客户端代码如下:

客户端改动和前面服务端差不多,具体步骤都一样,除了不能指定校验策略之外基本一样。

func main() {
 // 加载客户端证书
 certificate, err := tls.LoadX509KeyPair("x509/client.crt", "x509/client.key")
 if err != nil {
  log.Fatal(err)
 }

 // 构建CertPool以校验服务端证书有效性
 b, err := ioutil.ReadFile("./x509/rootCa.crt")
 if err != nil {
  log.Fatal(err)
 }
 cp := x509.NewCertPool()
 if !cp.AppendCertsFromPEM(b) {
  log.Fatal("credentials: failed to append certificates")
 }

 creds := credentials.NewTLS(&tls.Config{
  Certificates: []tls.Certificate{certificate},
  ServerName:   "www.example.com",
  RootCAs:      cp,
 })

 conn, err := grpc.Dial("localhost:8009", grpc.WithTransportCredentials(creds))
 if err != nil {
  panic(err)
 }
 defer conn.Close()

 client := pb.NewOrderManagementClient(conn)

 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 defer cancel()

 // Get Order
 retrievedOrder, err := client.GetOrder(ctx, &wrapperspb.StringValue{Value: "101"})
 if err != nil {
  panic(err)
 }

 log.Print("GetOrder Response -> : ", retrievedOrder)
}

可能遇到的问题

报错:transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use SANs instead

如果出现上述报错,是因为 go 1.15 版本开始废弃 CommonName[4],因此推荐使用 SAN 证书。如果想兼容之前的方式,需要设置环境变量 GODEBUG 为 GODEBUG=x509ignoreCN=0

什么是 SAN?SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

SAN 证书包含 Subject Alternative Name 部分

openssl x509 -text -noout -in server.crt | grep -A 1 "Subject Alternative Name"
            X509v3 Subject Alternative Name:
                DNS:www.example.com, DNS:localhost, DNS:127.0.0.1, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1

证书生成

标签:通信安全,err,证书,gRPC,server,x509,go,服务端,客户端
From: https://www.cnblogs.com/cheyunhua/p/17456964.html

相关文章

  • 算法 in Golang:D & C(分而治之)
    算法inGolang:D&C(分而治之)D&C算法(策略)Divide&Conquer属于递归算法的一种其实它更像是一种思路、策略递归递归Recursion基线条件BaseCase递归条件RecursiveCaseD&C的步骤找到一个简单的基线条件(BaseCase)把问题分开处理,直到它变为基线条件例......
  • LGOI 2023 游记
    前言比赛是深圳市龙岗区的小学生信息学奥林匹克竞赛,下面讲题时会概括题目,要原题的找我,如果要的人多会放在评论区。说真的,没想到疫情三年后的第一场比赛就这么水,8:30进去的,9:10出来的,40分钟光速AK。1.篮球赛比分给你一场篮球比赛的得分情况,如A1B3A2B1A3B3B1#表示A队分......
  • Golang Web--中间件的学习总结
    关于在golang中的中间件,可以翻阅笔者之前的博客,在这里不予详细说明。这里简单介绍下中间件的高级用法:1.如果每个路由分别要配置多个中间价,该如何处理2.如果有多个路由分别要配置多个中间件,该如何处理3.尝试不定义结构体开发中间件packagemainimport("context""......
  • gorm 版本对比
    两个版本github.com/jinzhu/gormv1.9.16gorm.io/gormv1.21.3 Open//jinzhufuncOpen(dialectstring,args...interface{})(db*DB,errerror){}//grom.iofuncOpen(dialectorDialector,opts...Option)(db*DB,errerror){} Find//jinzhufunc......
  • 推荐 7 款类似Google Analytics 的开源程序
    GoogleAnalytics我想大家应该都不会太陌生,它是Google提供的一个优秀Web分析数据服务,是最广泛使用的网站统计服务,能生成网站流量和来源详细统计数据。但GoogleAnalytics的最大缺点是它被Google控制,对很多人来说,这一点让他们非常不自在。而且GoogleAnalytics不是一个开源的......
  • django 自定义FileField upload_to上传路径
    defuser_directory_path(instance,name):"""clean_data内容:fork,vinclean_data:K:fileV:record1301DL00220230602全部.txtK:nameV:record1301DL00220230602全部.txt"""filename=name[15:2......
  • XSLT 介绍与 Google Ajaxslt
    最近研究ajaxslt的心得[url]http://bright895.blog.163.com/blog/static/182699233201142685114192/[/url]XSLT的英文标准名称为eXtensibleStylesheetLanguageTransformation。根据W3C的规范说明书(http://www.w3.org/TR/xslt),[color=red]最早设计XSLT的用意是帮助XML文档(doc......
  • 35 个你也许不知道的 Google 开源项目
    [b][color=red]以下是原文的复制,缺少许多超链接,最好查看原文,方便查看超链接内容.[/color][/b]Google是支持开源运动的最大公司之一,它们现在总共发布有超过500个的开源项目(大部分都是利用它们的API来完成),本文将列举一些有趣的开源项目,其中很可能有不少你不知道的哦。文本文件处......
  • google gson学习
    [color=red][b]googlegson学习[/b][/color][url]http://lyking2001.iteye.com/blog/504156[/url]Gson网址http://code.google.com/p/google-gson/[b]1.简单的处理list和map[/b]Gsongson=newGson();ListtestList=newArrayList();testList.a......
  • 这一次,带你玩转gRPC框架
    前言大家好,先做一下自我介绍我叫BarryYan,目前是一名互联网公司的研发工程师,同时也是后端技术领域的狂热爱好者和技术博主,在GitHub、CSDN社区、51CTO博客社区、阿里云技术社区、掘金技术社区和InfoQ写作社区等都有自己的博客,原创200余篇。虽然刚刚大学本科毕业不到一年,但是算上实......