gRPC — SSL/TLS单向认证、双向认证、Token认证

这篇具有很好参考价值的文章主要介绍了gRPC — SSL/TLS单向认证、双向认证、Token认证。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么是SSL/TLS?

传输层安全性协议(Transport Layer Security,缩写作 TLS ),其前身安全套接层(Secure Sockets Layer,缩写作 SSL )是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。根据传输层安全协议的规范,客户端与服务端的连接安全应该具备连接是私密的或连接是可靠的一种以上的特性。
SSL/TLS 协议通过 X.509 证书的数字文档将网站的公司实体信息绑定到加密密钥,每一个密钥对都有一个私有密钥和一个公有密钥。私有密钥是独有的,一般位于服务器上,用于解密由公有密钥加密过的信息,公有密钥是公开的,与服务器进行交互的每个人都可以持有公有密钥,用公有密钥加密的信息只能由私有密钥来解密。

SSL/TLS 协议提供以下的服务:

认证用户与服务器,确保数据发送到正确的客户端和服务器;

加密数据以防止数据在传输过程中被窃取;

维护数据的完整性,确保数据在传输过程中不被改变。

二、什么是 SAN?

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

三、gRPC 的 SSL/TLS 加密认证的实现

该部分内容基于上一个博客gRPC-入门示例的代码,进行进一步实现!如图所示,回顾上一个博客中gRPC入门示例代码,可以看出客户端和服务端之间无任务加密认证的过程,具有不安全性。因此,接下来的内容将在原有代码基础上,分别实现SSL/TLS的单向认证、双向认证、Token认证的过程,增加安全性。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

1、生成自签证证书

CA 是一个受信任的实体,它管理和发布用于公共网络中安全通信的安全证书和公钥。由该受信任的实体所签署或颁发的证书称为 CA 签名的证书。需要使用根证书CA来生成服务端、客户端证书。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

使用 OpenSSL 开源工具集创建一个 CA 根证书

根证书(root certificate)是属于根证书颁发机构(CA)的公钥证书。可以通过验证 CA 的签名从而信任 CA ,任何人都可以得到 CA 的证书(含公钥),用以验证它所签发的证书(客户端、服务端)。

1、去openssl的官网下载openssl,安装完成后,在终端运行以下命令,可以看到相应的版本号。

C:\Users\xxxxx>openssl -version
OpenSSL 3.2.1 30 Jan 2024 (Library: OpenSSL 3.2.1 30 Jan 2024)

2、在原有项目工程下,新建一个目录cert目录,进入该目录下,打开终端运行以下命令:

openssl genrsa -out ca.key 2048

最终,在cert目录将会生成ca.key的文件。

3、在cert目录下,新建 ca.conf文件,并且写入以下内容:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = ChongQing
localityName                = Locality Name (eg, city)
localityName_default        = ChongQing
organizationName            = Organization Name (eg, company)
organizationName_default    = JiangShui
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = jiangshui

然后运行在终端运行以下命令,创建ca证书签发请求,得到ca.csr

openssl req -new -sha256 -out ca.csr -key ca.key -config ca.conf

如下图所示,运行命令后,填写内容均默认,全部按下enter即可,全部确认完毕后,cert目录下将会生成对应ca.csr文件。

4、在终端下运行以下命令,通过ca.key和ca.csr生成ca根证书,得到ca.crt

openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

2、单向认证

生成服务端证书

1、运行以下命令,生成服务端私钥,server.key

openssl genrsa -out server.key 2048

2、在cert目录下,新建server.conf文件,并且写入以下内容:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = ChongQing
localityName                = Locality Name (eg, city)
localityName_default        = ChongQing
organizationName            = Organization Name (eg, company)
organizationName_default    = JiangShui
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = jiangshui

[ req_ext ]
# 添加subjectAltName
subjectAltName = @alt_names
# 文件末尾添加. www.p-pp.cn代表允许的ServerName,自己随便写
[alt_names]
DNS.1   = www.jiangshui.cn
DNS.2   = www.libai.cn
IP      = 127.0.0.1

然后运行在终端运行以下命令,创建证书签发请求,得到server.csr

openssl req -new -sha256 -out server.csr -key server.key -config server.conf

3、在终端运行以下命令,用CA证书和server.key、server.csr生成服务端证书(公钥),得到server.pem

openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.pem -extensions req_ext -extfile server.conf

4、完成以上步骤后,查看cert目录,是否存在以下图片中所示的文件,如果对应上,则说明生成各文件完毕。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

修改服务端代码

修改后服务端grpc_server.go代码如下:

package main

import (
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc1/service"
	"net"
)

func main() {
	//引入TLS认证相关文件(传入公钥和私钥)
	//配置 TLS认证相关文件
	creds, err := credentials.NewServerTLSFromFile("./cert/server.pem", "./cert/server.key")

	if err != nil {
		grpclog.Fatal("添加证书失败", err)
	}

	//获取grpc服务端,添加TLS认证
	rpcServer := grpc.NewServer(grpc.Creds(creds))

	//服务端映射(注册)到结构体,调用product_grpc.pb.go中的RegisterProductServiceServer方法
	service.RegisterProductServiceServer(rpcServer, service.ProductService)
	// 建立tcp端口监听
	listener, err := net.Listen("tcp", ":8002")
	if err != nil {
		grpclog.Fatal("启动监听出错", err)
	}
	//服务端监听tcp连接
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("启动服务出错", err)
	}

}

如图所示,此时,启动服务端代码,然后启动客户端调用服务端方法查询库存,出现失败。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

修改客户端代码

修改后服务端grpc_client.go代码如下:

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc1/service"
)

func main() {
	//先创建证书池,读取并解析公开证书,创建启用 TLS 的证书(公钥域名)
	creds, err := credentials.NewClientTLSFromFile("./cert/server.pem", "www.jiangshui.cn")
	if err != nil {
		grpclog.Fatal("加载pem失败", err)
	}
	//建立连接并添加传输凭证
	conn, err := grpc.Dial(":8002", grpc.WithTransportCredentials(creds))
	if err != nil {
		grpclog.Fatal("did not connect", err)
	}

	//关闭连接
	defer conn.Close()

	//调用product_grpc.pb.go中的NewProductServiceClient方法建立客户端
	prodClient := service.NewProductServiceClient(conn)
	request := &service.ProductRequest{
		ProdId: 123,
	}

	//远程调用方法GetProductStock
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {

		grpclog.Fatal("查询库存失败", err)

	}
	fmt.Println("查询成功", stockResponse.ProdStock)

}

如图所示,此时,启动服务端代码,然后启动客户端调用服务端方法查询库存,查询成功,实现了一个单向认证。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

3、双向认证

双向认证需要在单向认证的基础上,继续生成客户端证书,然后对客户端和服务端代码进一步整改,才能完成双向认证。本人在实施双向认证的时候,也遇到了许多问题。当我利用OpenSSL 开源工具集生成自签证证书,然后按步骤生成完客户端和服务端公私钥,最后修改客户端和服务端双向认证代码并且运行测试后,始终出现以下问题:

FATAL: 查询库存出错rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: 
x509: certificate signed by unknown authority (possibly because of \"x509: invalid signature: parent certificate cannot sign this kind of certificate\" while trying to verify candid
ate authority certificate \"jiangshui.com\")"

该错误在我多次尝试解决该bug,甚至直接搬用别人的代码直接使用也未能解决!其中包括参考了许多博客的教程以及码神之路的教程,都未能解决相关问题!

参考博客1:gRPC教程 — TLS单向认证、双向认证、Token认证、拦截器

参考博客2:【gRPC】自签CA、服务端和客户端双向认证

参考博客3:码神之路教程

大家可以使用以上博客教程中的其中方法先进行尝试,看看如果能够测试成功,自然皆大欢喜!如果测试失败,出现与我相同错误!接下来,我将提供一种与以上博客不同的方法来实现双向认证过程!与上述博客中描述的方法不同,此处我不在利用OpenSSL 开源工具集生成自签证书以及服务端、客户端的公私钥。此处,我将直接使用go语言代码和相关库实现自签证书以及服务端、客户端的公私钥生成。

用go代码生成自签证书和客户端、服务端公私钥

在cert文件目录新建一个cert.go文件,并且在文件中写入以下代码:

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"os"
	"time"
)

func main() {
	// 生成CA证书
	caCert, caPrivateKey, err := generateCACertificate()
	if err != nil {
		panic(err)
	}

	// 生成服务端证书
	err = generateCert("server.crt", "server.key", caCert, caPrivateKey, "localhost", true)
	if err != nil {
		panic(err)
	}

	// 生成客户端证书
	err = generateCert("client.crt", "client.key", caCert, caPrivateKey, "Client", false)
	if err != nil {
		panic(err)
	}
}

func generateCACertificate() (*x509.Certificate, *ecdsa.PrivateKey, error) {
	caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, nil, err
	}

	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Example CA"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(10, 0, 0), // 10 years
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	caCertBytes, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caPrivateKey.PublicKey, caPrivateKey)
	if err != nil {
		return nil, nil, err
	}

	caCert, err := x509.ParseCertificate(caCertBytes)
	if err != nil {
		return nil, nil, err
	}

	// Save the CA certificate
	saveCert("ca.crt", caCertBytes)
	// Save the CA private key
	savePrivateKey("ca.key", caPrivateKey)

	return caCert, caPrivateKey, nil
}

func generateCert(certFilename, keyFilename string, caCert *x509.Certificate, caPrivateKey *ecdsa.PrivateKey, commonName string, isServer bool) error {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return err
	}

	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return err
	}

	certTemplate := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{"Example Org"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(1, 0, 0), // 1 year
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}

	// 为证书添加SANs字段
	certTemplate.DNSNames = append(certTemplate.DNSNames, "localhost")
	certTemplate.IPAddresses = append(certTemplate.IPAddresses, net.ParseIP("127.0.0.1"))

	if isServer {
		certTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}

	certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, &privateKey.PublicKey, caPrivateKey)
	if err != nil {
		return err
	}

	saveCert(certFilename, certBytes)
	savePrivateKey(keyFilename, privateKey)

	return nil
}

func saveCert(filename string, certBytes []byte) {
	certPEMFile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer certPEMFile.Close()

	pem.Encode(certPEMFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
}

func savePrivateKey(filename string, privateKey *ecdsa.PrivateKey) {
	keyFile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer keyFile.Close()

	privBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		panic(err)
	}
	pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
}

代码中的caTemplate、certTemplate部分类似于OpenSSL 开源工具集生成证书,相关部分可以根据自己的需求进行修改!此外,证书保存的路径根据自己的具体情况,进行修改!或者生成完证书后,自己将证书文件移动到对应的目录下。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc
此处图片中的内容则是类似于OpenSSL 开源工具集生成SAN证书部分,添加SANs字段,DNSName可以根据自身需求填写(添加多个)。

修改服务端代码

server_grpc.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc/grpclog"
	"grpc2/service"
	"io/ioutil"
	"net"

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

func main() {
	// 证书认证-双向认证
	// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	cert, err := tls.LoadX509KeyPair("./cert/server.crt", "./cert/server.key")
	if err != nil {
		grpclog.Fatalf("failed to load server certificate: %v", err)
	}

	caCert, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("failed to read ca certificate: %v", err)
	}
	// 创建一个新的、空的证书池
	caCertPool := x509.NewCertPool()
	// 附加来自 CA 的证书
	if !caCertPool.AppendCertsFromPEM(caCert) {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 构建基于 TLS 的 TransportCredentials 选项
	config := &tls.Config{
		Certificates: []tls.Certificate{cert},        // 服务器的证书和私钥
		ClientAuth:   tls.RequireAndVerifyClientCert, //启用并要求客户端证书验证
		ClientCAs:    caCertPool,                     //用于验证客户端证书的 CA 证书
	}

	listener, err := net.Listen("tcp", ":50051")
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	rpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(config)))
	// 在此注册您的 gRPC 服务处理程序

	service.RegisterProductServiceServer(rpcServer, service.ProductService)

	if err != nil {
		grpclog.Fatal("启动监听出错", err)
	}
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("启动服务出错", err)
	}
	fmt.Println("启动grpc服务端成功")
}

修改客户端代码

client_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc2/service"
	"io/ioutil"
)

func main() {
	// 证书认证-双向认证
	// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	clientCert, err := tls.LoadX509KeyPair("./cert/client.crt", "./cert/client.key")
	if err != nil {
		grpclog.Fatalf("failed to load client certificate: %v", err)
	}

	// 创建一个新的、空的证书池
	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("could not read ca certificate: %v", err)
	}

	// 附加来自 CA 的证书
	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 构建基于 TLS 的 TransportCredentials 选项
	config := &tls.Config{
		Certificates: []tls.Certificate{clientCert}, // 客户端的证书和私钥
		ServerName:   "localhost",                   //验证DNSName
		RootCAs:      certPool,                      // CA证书用于验证服务器证书
	}

	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)))
	if err != nil {
		grpclog.Fatalf("failed to dial server: %v", err)
	}
	defer conn.Close()

	// 现在您可以使用 `conn` 创建客户端并进行 gRPC 调用
	prodClient := service.NewProductServiceClient(conn)

	request := &service.ProductRequest{
		ProdId: 123,
	}
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {
		grpclog.Fatal("查询库存出错", err)
	}
	fmt.Println("查询成功", stockResponse.ProdStock)
}

客户端和服务端代码修改完成后,启动服务端,运行客户端进行测试,此时不再出现之前所遇到的错误。如下图所示,成功调用到获取库存的方法,查询库存成功!
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

4、token认证

gRPC为每个gRPC方法调用提供了Token认证支持,可以基于用户传入的Token判断用户是否登陆、以及权限等。

服务端添加用户名密码的校验

server_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"grpc2/service"
	"io/ioutil"
	"net"

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

func main() {
	// 证书认证-双向认证
	// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	cert, err := tls.LoadX509KeyPair("./cert/server.crt", "./cert/server.key")
	if err != nil {
		grpclog.Fatalf("failed to load server certificate: %v", err)
	}

	caCert, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("failed to read ca certificate: %v", err)
	}
	// 创建一个新的、空的证书池
	caCertPool := x509.NewCertPool()
	// 附加来自 CA 的证书
	if !caCertPool.AppendCertsFromPEM(caCert) {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 构建基于 TLS 的 TransportCredentials 选项
	config := &tls.Config{
		Certificates: []tls.Certificate{cert},        // 服务器的证书和私钥
		ClientAuth:   tls.RequireAndVerifyClientCert, //启用并要求客户端证书验证
		ClientCAs:    caCertPool,                     //用于验证客户端证书的 CA 证书
	}

	listener, err := net.Listen("tcp", ":50051")
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	//实现token认证,需要合法的用户名和密码
	//实现一个拦截器
	var authInterceptor grpc.UnaryServerInterceptor
	authInterceptor = func(
		ctx context.Context,
		req any,
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (resp any, err error) {
		//拦截普通方法请求,验证Token
		err = Auth(ctx)
		if err != nil {
			return
		}
		return handler(ctx, req)
	}

	//添加在服务端拦截器
	rpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(config)), grpc.UnaryInterceptor(authInterceptor))
	// 在此注册您的 gRPC 服务处理程序

	service.RegisterProductServiceServer(rpcServer, service.ProductService)

	if err != nil {
		grpclog.Fatal("启动监听出错", err)
	}
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("启动服务出错", err)
	}
	fmt.Println("启动grpc服务端成功")
}

func Auth(ctx context.Context) error {
	//拿到传输的用户名和密码
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return fmt.Errorf("missing credentials")
	}
	var user string
	var password string

	if val, ok := md["user"]; ok {
		user = val[0]
	}
	if val, ok := md["password"]; ok {
		password = val[0]
	}

	if user != "admin" || password != "admin" {
		return status.Errorf(codes.Unauthenticated, "token不合法")
	}

	return nil
}

此时,启动客户端远程调用方法失败,出现token不合法的提示,如下图所示
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

修改客户端代码,以适应token认证

客户段需要携带token进行传入,使得服务端能进行正确验证,因此需要修改客户端代码,其中主要修改以下代码:

//客户端携带token传入
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)), grpc.WithPerRPCCredentials(token))

通过grpc.WithPerRPCCredentials(token)函数携带token。通过追踪该函数,可得知该函数接收的参数为一个接口,因此客户端传入的token,需要定义一个结构体将数据传入,并且将该结构体需要实现grpc.PerRPCCredentials接口。

type PerRPCCredentials interface {
	// 返回需要认证的必要信息
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
  // 是否使用安全链接(TLS)
	RequireTransportSecurity() bool
}

实现grpc.PerRPCCredentials接口

在原来项目的基础上,新建auth文件目录,并且新建auth.go文件,写入以下代码实现相关接口:

package auth

import "context"

type Authentication struct {
	User     string
	Password string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
	map[string]string, error,
) {
	return map[string]string{"user": a.User, "password": a.Password}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

修改客户端代码

client_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc2/grpc_client/auth"
	"grpc2/service"
	"io/ioutil"
)

func main() {
	// 证书认证-双向认证
	// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	clientCert, err := tls.LoadX509KeyPair("./cert/client.crt", "./cert/client.key")
	if err != nil {
		grpclog.Fatalf("failed to load client certificate: %v", err)
	}

	// 创建一个新的、空的证书池
	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("could not read ca certificate: %v", err)
	}

	// 附加来自 CA 的证书
	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 构建基于 TLS 的 TransportCredentials 选项
	config := &tls.Config{
		Certificates: []tls.Certificate{clientCert}, // 客户端的证书和私钥
		ServerName:   "localhost",                   //验证DNSName
		RootCAs:      certPool,                      // CA证书用于验证服务器证书
	}
	token := &auth.Authentication{
		User:     "admin",
		Password: "admin",
	}
	//客户端携带token传入
	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)), grpc.WithPerRPCCredentials(token))
	if err != nil {
		grpclog.Fatalf("failed to dial server: %v", err)
	}
	defer conn.Close()

	// 现在您可以使用 `conn` 创建客户端并进行 gRPC 调用
	prodClient := service.NewProductServiceClient(conn)

	request := &service.ProductRequest{
		ProdId: 123,
	}
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {
		grpclog.Fatal("查询库存出错", err)
	}
	fmt.Println("查询成功", stockResponse.ProdStock)
}

最后,启动服务端,运行客户端代码进行测试。当携带的token与服务端要求的不一致时,出现下图所示的token不合法,查询失败。
使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc

当携带的token与服务端要求的一致时,出现下图所示查询成功!使客户端支持 tls 单向身份验证,ssl,网络,服务器,go,rpc文章来源地址https://www.toymoban.com/news/detail-845754.html

到了这里,关于gRPC — SSL/TLS单向认证、双向认证、Token认证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA使用RestTemplate类实现SSL双向/单向认证(国际)

    以管理员身份打开Windows PowerShel,通过cd(与linux系统类似)命令进入到JDK的bin目录:如C:Program FilesJavajdk1.8.0_221jrebin,找到目录下有keytool.exe就是正确进入目录了 参数说明: genkey 表示要创建一个新的密钥 alias 表示 keystore 的别名、 keyalg 表示使用的加密算法是 RSA ,一种非

    2024年02月15日
    浏览(40)
  • openssl自签名CA根证书、服务端和客户端证书生成并模拟单向/双向证书验证

    1.1 生成CA证书私钥 openssl genrsa -aes256 -out ca.key 2048 1.2 取消密钥的密码保护 openssl rsa -in ca.key -out ca.key 1.3 生成根证书签发申请文件(csr文件) openssl req -new -sha256 -key ca.key -out ca.csr -subj \\\"/C=CN/ST=FJ/L=XM/O=NONE/OU=NONE/CN=localhost/emailAddress=test@test.com\\\" 上述参数含义 req----执行证书签发命令

    2024年04月25日
    浏览(42)
  • emqx 配置ssl/tls 双向认证(亲自测试有效)

    bash脚本,生成自签名ca、服务端、客户端的key和证书 openssl.cnf配置文件 验证证书是否有效 将证书文件拷贝到emqxetccerts目录下(默认目录),并修改配置文件emqx.conf。SSL/TLS 双向连接的启用及验证 mqttx连接验证 出现连接成功,代表测试无问题  

    2024年03月11日
    浏览(41)
  • 基于SSL/TLS双向安全连接设备CA证书认证

    小伙伴儿们,如果觉得文章干货满满,欢迎加入公众号【编程识堂】,更多干货等你们来哦! 设备证书是由CA根证书签发给客户端设备使用的数字证书,用于客户端和服务端连接时,服务端对客户端进行安全认证。认证通过后服务端和客户端可基于证书内的加密密钥进行安全

    2024年01月20日
    浏览(47)
  • Kubernetes客户端认证——基于CA证书的双向认证方式

    Kubernetes集群的访问权限控制由API Server负责,API Server的访问权限控制由身份验证(Authentication)、授权(Authorization)和准入控制(Admission control)三个步骤组成,这个三个步骤是按序进行的(详细介绍请参见《(转)使用kubectl访问Kubernetes集群时的身份验证和授权》)。 其中身份验证

    2023年04月10日
    浏览(38)
  • [Kafka集群] 配置支持Brokers内部SSL认证\外部客户端支持SASL_SSL认证并集成spring-cloud-starter-bus-kafka

    目录 Kafka 集群配置 准备 配置流程 Jaas(Java Authentication and Authorization Service )文件 zookeeper 配置文件 SSL自签名 启动zookeeper集群 启动kafka集群  spring-cloud-starter-bus-kafka 集成 下载统一版本Kafka服务包至三台不同的服务器上 文章使用版本为  kafka_2.13-3.5.0.tgz 下载地址 jdk版本 为 Ado

    2024年02月04日
    浏览(56)
  • HTTPS单向认证与双向认证

    Https就是HTTP+SSL/TSL的简称。 SSL(Secure Socket Layer 安全套接层)是TCP/IP协议中基于HTTP之下TCP之上的一个可选协议层。 起初HTTP在传输数据时使用的是明文,传输过程中并不安全。网景(Netscap)公司推出了SSL解决了这一安全隐患,因此越来越多的人也开始使用HTTPS。在SSL更新到3.0时,

    2024年02月11日
    浏览(40)
  • 【网络】https单向认证和双向认证

    之前面试的时候,面试官问我了解过https的双向认证吗?当时,的确不理解。不过没关系,现在就来补上。 1.单向认证 还是有必要先说下单向认证,单向认证是我刚开始接触https的时候就了解到的。下面看一下执行流程图 (图是网上找的) 再用文字描述下: 首先建立链接  - 验

    2023年04月09日
    浏览(51)
  • NGINX自建SSL实现-单向、双向加密,中文SSL证书,吊销列表实现

    对称加密算法 对称加密算法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。 非对称加密算法 非对称加密算法(RSA)是内容加密的一类算法,它有两个秘钥:公钥与私钥。公钥是公开的钥匙,所有人都可以知道,私钥是保密的

    2024年02月03日
    浏览(42)
  • ssl单向证书和双向证书校验测试及搭建流程

    首先了解下HTTP和HTTPS的区别: HTTPS与HTTP有什么不同? HTTP是过去很长一段时间我们经常用到的一种传输协议。HTTP协议传输的数据都是未加密的,这就意味着用户填写的密码、账号、交易记录等机密信息都是明文,随时可能被泄露、窃取、篡改,从而被黑客加以利用,因此使用

    2024年02月13日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包