1. 程式人生 > 其它 >grpc-tts證書認證

grpc-tts證書認證

grpc-tts證書認證

一、前言

前面篇章的gRPC都是明文傳輸的,容易被篡改資料。本章將介紹如何為gRPC新增安全機制,包括TLS證書認證和Token認證。

二、TLS證書認證

什麼是TLS

TLS(Transport Layer Security,安全傳輸層),TLS是建立在傳輸層TCP協議之上的協議,服務於應用層,它的前身是SSL(Secure Socket Layer,安全套接字層),它實現了將應用層的報文進行加密後再交由TCP進行傳輸的功能。

TLS的作用

TLS協議主要解決如下三個網路安全問題。

  • 保密(message privacy),保密通過加密encryption實現,所有資訊都加密傳輸,第三方無法嗅探;
  • 完整性(message integrity),通過MAC校驗機制,一旦被篡改,通訊雙方會立刻發現;
  • 認證(mutual authentication),雙方認證,雙方都可以配備證書,防止身份被冒充;

官方grpc-eg

原始碼

三、生成私鑰

生成RSA私鑰:openssl genrsa -out server.key 2048

生成RSA私鑰,命令的最後一個引數,將指定生成金鑰的位數,如果沒有指定,預設512

生成ECC私鑰:openssl ecparam -genkey -name secp384r1 -out server.key

生成ECC私鑰,命令為橢圓曲線金鑰引數生成及操作,本文中ECC曲線選擇的是secp384r1

四、生成公鑰

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

openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰檔案、-x509指輸出證書、-days 3650為有效期

此後則輸入證書擁有者資訊

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:XxXx
Locality Name (eg, city) []:XxXx
Organization Name (eg, company) [Internet Widgits Pty Ltd]:XX Co. Ltd
Organizational Unit Name (eg, section) []:Dev
Common Name (e.g. server FQDN or YOUR name) []:go-grpc-example
Email Address []:[email protected]

執行指令碼生成:

openssl.cnf

[req]
distinguished_name = req_distinguished_name
attributes = req_attributes

[req_distinguished_name]

[req_attributes]

[test_ca]
basicConstraints        = critical,CA:TRUE
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer:always
keyUsage                = critical,keyCertSign

[test_server]
basicConstraints        = critical,CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = critical,digitalSignature,keyEncipherment,keyAgreement
subjectAltName          = @server_alt_names

[server_alt_names]
DNS.1 = *.test.example.com

[test_client]
basicConstraints        = critical,CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = critical,nonRepudiation,digitalSignature,keyEncipherment
extendedKeyUsage        = critical,clientAuth

create_tls.sh

#!/bin/bash

# Create the server CA certs.
openssl req -x509                                     \
  -newkey rsa:4096                                    \
  -nodes                                              \
  -days 3650                                          \
  -keyout ca_key.pem                                  \
  -out ca_cert.pem                                    \
  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-server_ca/   \
  -config ./openssl.cnf                               \
  -extensions test_ca                                 \
  -sha256

# Create the client CA certs.
openssl req -x509                                     \
  -newkey rsa:4096                                    \
  -nodes                                              \
  -days 3650                                          \
  -keyout client_ca_key.pem                           \
  -out client_ca_cert.pem                             \
  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client_ca/   \
  -config ./openssl.cnf                               \
  -extensions test_ca                                 \
  -sha256

# Generate a server cert.
openssl genrsa -out server_key.pem 4096
openssl req -new                                    \
  -key server_key.pem                               \
  -days 3650                                        \
  -out server_csr.pem                               \
  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-server1/   \
  -config ./openssl.cnf                             \
  -reqexts test_server
openssl x509 -req           \
  -in server_csr.pem        \
  -CAkey ca_key.pem         \
  -CA ca_cert.pem           \
  -days 3650                \
  -set_serial 1000          \
  -out server_cert.pem      \
  -extfile ./openssl.cnf    \
  -extensions test_server   \
  -sha256
openssl verify -verbose -CAfile ca_cert.pem  server_cert.pem

# Generate a client cert.
openssl genrsa -out client_key.pem 4096
openssl req -new                                    \
  -key client_key.pem                               \
  -days 3650                                        \
  -out client_csr.pem                               \
  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client1/   \
  -config ./openssl.cnf                             \
  -reqexts test_client
openssl x509 -req           \
  -in client_csr.pem        \
  -CAkey client_ca_key.pem  \
  -CA client_ca_cert.pem    \
  -days 3650                \
  -set_serial 1000          \
  -out client_cert.pem      \
  -extfile ./openssl.cnf    \
  -extensions test_client   \
  -sha256
openssl verify -verbose -CAfile client_ca_cert.pem  client_cert.pem

rm *_csr.pem

五、新建proto檔案

主要是定義我們服務的方法以及資料格式,建立simple.proto檔案。

1.定義傳送訊息的資訊

message SimpleRequest{
    // 定義傳送的引數,採用駝峰命名方式,小寫加下劃線,如:student_name
    string data = 1;//傳送資料
}

2.定義響應資訊

message SimpleResponse{
    // 定義接收的引數
    // 引數型別 引數名 標識號(不可重複)
    int32 code = 1;  //狀態碼
    string value = 2;//接收值
}

3.定義服務方法Route

// 定義我們的服務(可定義多個服務,每個服務可定義多個介面)
service Simple{
    rpc Route (SimpleRequest) returns (SimpleResponse){};
}

4.編譯proto檔案

syntax = "proto3";// 協議為proto3

//option go_package = "path;name";
//path 表示生成的go檔案的存放地址,會自動生成目錄的。
//name 表示生成的go檔案所屬的包名

//  生成pb.go命令:  protoc -I ./ --go_out=plugins=grpc:.\07tlssecurity\proto\  .\07tlssecurity\proto\simple.proto

option go_package = "./;proto";
package proto;

// 定義我們的服務(可定義多個服務,每個服務可定義多個介面)
service Simple{
  rpc Route (SimpleRequest) returns (SimpleResponse){};
}

// 定義傳送請求資訊
message SimpleRequest{
  // 定義傳送的引數,採用駝峰命名方式,小寫加下劃線,如:student_name
  // 引數型別 引數名 標識號(不可重複)
  string data = 1;
}

// 定義響應資訊
message SimpleResponse{
  // 定義接收的引數
  // 引數型別 引數名 標識號(不可重複)
  int32 code = 1;
  string value = 2;
}


編譯

// 指令編譯方法,進入go-grpc-example專案,執行
go-grpc-example> protoc -I ./ --go_out=plugins=grpc:.\07tlssecurity\proto\  .\07tlssecurity\proto\simple.proto

六、服務端構建TLS證書並認證

1.定義我們的服務

package main

import (
	"context"
	pb "go-grpc-example/07tlssecurity/proto"
	"log"
	"net"

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

/*
@author RandySun
@create 2022-05-08-17:03
*/

// SimpleService 定義我們的服務
type SimpleService struct {
}

// Route 實現Route方法
func (s *SimpleService) Route(ctx context.Context, req *pb.SimpleRequest) (*pb.SimpleResponse, error) {
		res := pb.SimpleResponse{
		Code:  200,
		Value: "hello " + req.Data,
	}
	return &res, nil
}

const (
	// Address 監聽地址
	Address string = ":8001"
	// NetWork 網路通訊協議
	NetWork string = "tcp"
)

func main() {
	// 監聽本地埠
	listener, err := net.Listen(NetWork, Address)
	if err != nil {
		log.Fatalf("net.Listen err: %V", err)
	}

	// 從輸入證書檔案和金鑰檔案為服務端構造TLS憑證
	creds, err := credentials.NewServerTLSFromFile("../pkg/tls/server_cert.pem", "../pkg/tls/server_key.pem")
	if err != nil {
		log.Fatalf("Failed to generate credentials %v", err)
	}

	// 建立grpc服務例項
	grpcServer := grpc.NewServer(grpc.Creds(creds))

	// 在grpc伺服器註冊我們的服務
	pb.RegisterSimpleServer(grpcServer, &SimpleService{})
	log.Println(Address, "net.Listing whth TLS and token...")

	//用伺服器 Serve() 方法以及我們的埠資訊區實現阻塞等待,直到程序被殺死或者 Stop() 被呼叫
	err = grpcServer.Serve(listener)

	if err != nil {
		log.Fatalf("grpcService.Serve err:%v", err)
	}
	log.Println("grpcService.Serve run success")
}

  • credentials.NewServerTLSFromFile:從輸入證書檔案和金鑰檔案為服務端構造TLS憑證
  • grpc.Creds:返回一個ServerOption,用於設定伺服器連線的憑證。
go run server.go
2022/03/27 20:14:10 :8001 net.Listing...

七、客戶端配置TLS連線

package main

import (
	"context"
	pb "go-grpc-example/07tlssecurity/proto"
	"log"

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

/*
@author RandySun
@create 2022-05-08-17:03
*/
// Address 連線地址
const Address string = ":8001"

var grpcClient pb.SimpleClient

func main() {
	//從輸入的證書檔案中為客戶端構造TLS憑證
	creds, err := credentials.NewClientTLSFromFile("../pkg/tls/ca_cert.pem", "x.test.example.com")
	if err != nil {
		log.Fatalf("Failed to create TLS credentials %v", err)
	}

	// 連線伺服器
	conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatalf("net.Connect err: %v", err)
	}
	defer conn.Close()

	// 建立gRPC連線
	grpcClient = pb.NewSimpleClient(conn)
	route()
}

// route 呼叫服務端Route方法
func route() {
	// 建立傳送結構體
	req := pb.SimpleRequest{
		Data: "grpc",
	}
	// 呼叫我們的服務(Route方法)
	// 同時傳入了一個 context.Context ,在有需要時可以讓我們改變RPC的行為,比如超時/取消一個正在執行的RPC
	res, err := grpcClient.Route(context.Background(), &req)
	if err != nil {
		log.Fatalf("Call Route err: %v", err)
	}
	// 列印返回值
	log.Println(res)
}

執行客戶端

go run client.go
2022/03/27 20:20:39 服務的返回響應data: code:200 value:"hello grpc"
  • credentials.NewClientTLSFromFile:從輸入的證書檔案中為客戶端構造TLS憑證。
  • grpc.WithTransportCredentials:配置連線級別的安全憑證(例如,TLS/SSL),返回一個DialOption,用於連線伺服器。

到這裡,已經完成TLS證書認證了,gRPC傳輸不再是明文傳輸。此外,新增自定義的驗證方法能使gRPC相對更安全。

成功呼叫Server端的Route方法並獲取返回的資料。

八、總結

本篇介紹如何為gRPC新增TLS證書認證和自定義認證,從而讓gRPC更安全。

參考:gRPC官方文件中文版