Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

GRPC使用openssl证书

加密数据传输过程,保证调用的安全性。

证书生成

自签证书(root CA)

命令解析:https://blog.csdn.net/adminstate/article/details/128662641

  1. 生成私钥文件

    1
    openssl genrsa -des3 -out private.key 2048
  2. 创建证书请求

    1
    openssl req -new -key private.key -out ca.csr
  3. 生成ca.crt

    1
    openssl x509 -req -days 365 -in ca.csr -signkey private.key -out ca.crt

SAN证书

参考:https://blog.csdn.net/a145127/article/details/126311442

SAN证书需要上述生成的根证书。

  1. 设置配置文件

    • 终端openssl version -d可以查找配置文件
    • 修改配置文件
  2. 生成服务器密钥和证书

    1
    2
    3
    4
    5
    6
    7
    8
    # 生成服务器私钥,密码输入123456
    $ openssl genpkey -algorithm RSA -out ../server/server.key

    # 使用私钥来签名证书
    $ openssl req -new -nodes -key ../server/server.key -out ../server/server.csr -config openssl.cnf -extensions 'v3_req'

    # 生成SAN证书
    $ openssl x509 -req -in ../server/server.csr -out ../server/server.pem -CA ca.crt -CAkey private.key -CAcreateserial -extfile ./openssl.cnf -extensions 'v3_req'
  3. 生成客户端密钥和证书

    1
    2
    3
    4
    5
    6
    7
    8
    # 生成客户端私钥,密码输入123456
    $ openssl genpkey -algorithm RSA -out ../client/client.key

    # 使用私钥来签名证书
    $ openssl req -new -nodes -key ../client/client.key -out ../client/client.csr -config openssl.cnf -extensions 'v3_req'

    # 生成SAN证书
    $ openssl x509 -req -in ../client/client.csr -out ../client/client.pem -CA ca.crt -CAkey private.key -CAcreateserial -extfile ./openssl.cnf -extensions 'v3_req'

目录结构

截屏2023-06-22 10.15.43

单项认证

  1. 服务端使用证书和私钥

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package main

    import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    pd "grpc_helloworld/server/proto"
    "log"
    "net"
    )

    type Server struct {
    pd.UnimplementedHelloServerServer
    }

    func (s *Server) Say(ctx context.Context, q *pd.HelloReq) (*pd.HelloRep, error) {
    return &pd.HelloRep{
    Say: fmt.Sprintf("%s已经%d岁了", q.Name, q.Age),
    }, nil
    }
    func main() {
    // 读取证书
    file, _ := credentials.NewServerTLSFromFile("./server.pem", "./server.key")

    listen, _ := net.Listen("tcp", ":8080")
    // 创建服务并添加证书
    newServer := grpc.NewServer(grpc.Creds(file))
    pd.RegisterHelloServerServer(newServer, new(Server))
    err := newServer.Serve(listen)
    if err != nil {
    log.Print("err:", err)
    }
    }

  2. 客户端使用公钥(证书中有公钥)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package main

    import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    pd "grpc_helloworld/client/proto"
    "log"
    )

    func main() {
    // 添加证书
    file, err2 := credentials.NewClientTLSFromFile("../server/server.pem", "*.test.example.com")
    if err2 != nil {
    log.Fatal("证书错误", err2)
    }
    conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(file))

    if err != nil {
    fmt.Print(err)
    }
    defer conn.Close()
    client := pd.NewHelloServerClient(conn)
    feature, err := client.Say(context.Background(), &pd.HelloReq{
    Name: "hello",
    Age: 22,
    })
    if err != nil {
    log.Print(err)
    }
    fmt.Print(feature)
    }

双向认证

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func main() {
cert, err := tls.LoadX509KeyPair("./server.pem", "./server.key")
if err != nil {
log.Fatal("证书读取错误", err)
}
// 创建一个新的、空的 CertPool
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile("../encryption/ca.crt")
if err != nil {
log.Fatal("ca证书读取错误", err)
}
// 尝试解析所传入的 PEM 编码的证书。如果解析成功会将其加到 CertPool 中,便于后面的使用
certPool.AppendCertsFromPEM(ca)
// 构建基于 TLS 的 TransportCredentials 选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链,允许包含一个或多个
Certificates: []tls.Certificate{cert},
// 要求必须校验客户端的证书。可以根据实际情况选用以下参数
ClientAuth: tls.RequireAndVerifyClientCert,
// 设置根证书的集合,校验方式使用 ClientAuth 中设定的模式
ClientCAs: certPool,
})
listen, _ := net.Listen("tcp", ":8080")
// 创建服务并添加证书
newServer := grpc.NewServer(grpc.Creds(creds))

pd.RegisterHelloServerServer(newServer, new(Server))
err = newServer.Serve(listen)
if err != nil {
log.Print("err:", err)
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func main() {
// 添加证书
cert, _ := tls.LoadX509KeyPair("./client.pem", "./client.key")
// 创建一个新的、空的 CertPool
certPool := x509.NewCertPool()
ca, _ := ioutil.ReadFile("../encryption/ca.crt")
// 尝试解析所传入的 PEM 编码的证书。如果解析成功会将其加到 CertPool 中,便于后面的使用
certPool.AppendCertsFromPEM(ca)
// 构建基于 TLS 的 TransportCredentials 选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链,允许包含一个或多个
Certificates: []tls.Certificate{cert},
// 要求必须校验客户端的证书。可以根据实际情况选用以下参数
ServerName: "*.test.example.com",
RootCAs: certPool,
})
conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(creds))

if err != nil {
fmt.Print(err)
}
defer conn.Close()
client := pd.NewHelloServerClient(conn)
feature, err := client.Say(context.Background(), &pd.HelloReq{
Name: "hello",
Age: 22,
})
if err != nil {
log.Print(err)
}
fmt.Print(feature)
}

token认证

服务端校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
pd "grpc_helloworld/server/proto"
"log"
"net"
)

type Server struct {
pd.UnimplementedHelloServerServer
}

func (s *Server) Say(ctx context.Context, q *pd.HelloReq) (*pd.HelloRep, error) {
return &pd.HelloRep{
Say: fmt.Sprintf("%s已经%d岁了", q.Name, q.Age),
}, nil
}
func main() {
var authInterceptor grpc.UnaryServerInterceptor
authInterceptor = func(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error) {
//拦截普通方法请求,验证 Token
err = Auth(ctx)
if err != nil {
return
}
// 继续处理请求
return handler(ctx, req)
}

listen, _ := net.Listen("tcp", ":8080")
// 创建服务并添加证书
newServer := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor))

pd.RegisterHelloServerServer(newServer, new(Server))
err := newServer.Serve(listen)
if err != nil {
log.Print("err:", err)
}
}
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 != "123456" {
return status.Errorf(codes.Unauthenticated, "token不合法")
}
return nil
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pd "grpc_helloworld/client/proto"
"log"
)

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
}
func main() {
user := &Authentication{
User: "admin",
Password: "123456",
}
conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(user))

if err != nil {
fmt.Print(err)
}
defer conn.Close()
client := pd.NewHelloServerClient(conn)
feature, err := client.Say(context.Background(), &pd.HelloReq{
Name: "hello",
Age: 22,
})
if err != nil {
log.Print(err)
}
fmt.Print(feature)
}

评论