go client-server模式: grpc
什么是grpc?
grpc是google开发的一个Remote Procedure Call (RPC) framework。
生成TLS Certificate
- 生成ca(Certificate Authority)私钥和自签名证书
openssl req -x509 -newkey rsa:4096 -nodes -days 3650 -keyout ca-key.pem -out ca-cert.pem -subj "/C=TR/ST=ASIA/L=ISTANBUL/O=DEV/OU=TUTORIAL/CN=*.hack.com/emailAddress=hack@foxmail.com"
脚本返回结果:
winsun@unbuntu64:~/test/grpc-tls-go/cert$ ./generator.sh
[1] 删除所有pem文件
[2] 生成ca私钥和自签名证书
....+.....+....+..+.+...+..+.............+.........
...................................................
-----
winsun@unbuntu64:~/test/grpc-tls-go/cert$ ll
total 20
drwxrwxr-x 2 winsun winsun 4096 3月 14 11:01 ./
drwxrwxr-x 3 winsun winsun 4096 3月 14 10:51 ../
-rw-rw-r-- 1 winsun winsun 2139 3月 14 11:01 ca-cert.pem
-rw------- 1 winsun winsun 3272 3月 14 11:01 ca-key.pem
-rwxrwxr-x 1 winsun winsun 303 3月 14 11:01 generator.sh*
- -x509是公钥证书格式,也常被称为数字证书,自己拥有公钥可以进行签名校验
- -newkey rsa:4096, 同时提供4096比特的ras密钥和证书申请
- -nodes,不加密private key
- -days,证书有效期
- -keyout,创建的密钥存入ca-key.pem
- -out,证书存入ca-cert.pem
- -subj 命令解释如下:
/C=CN 国家
/ST=ZHEJIANG 省份
/L=HANGZHOU 本地城市
/O=HACK 组织
/OU=TUTORIAL 组织部门
/CN=*.hack.com 公共名或域名
/emailAddress=hack@tutorial.com email地址
- 生成server key和csr(certificate siging request)
openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=cn/ST=zhejiang/L=hangzhou/O=DEV/OU=BLOG/CN=*.hack.com/emailAddress=hack@foxmail.com"
server-req.pem并非证书,只是证书签名申请,如下所示:
winsun@unbuntu64:~/test/grpc-tls-go/cert$ cat server-req.pem
-----BEGIN CERTIFICATE REQUEST-----
MIIE3TCCAsUCAQAwgZcxCzAJBgNVBAYTAmNuMREwDwYDVQQIDAh6aGVqaWFuZzER
MA8GA1UEBwwIaGFuZ3pob3UxETAPBgNVBAoMCGZpZ2h0YmVlMREwDwYDVQQLDAhz
ZWN1cml0eTEXMBUGA1UEAwwOKi5maWdodGJlZS5jb20xIzAhBgkqhkiG9w0BCQEW
FHdpbnN1bnh1QGZveG1haWwuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAn58bmfJ3wguXKIAPOCNnRq/feMI5KOIYvj+1ZaMkLo9J22GELpqg1vuM
vyqNaUEXWC7p0pMzNKsShnIhSdNfNvjJw48SExtRgCaxM2zbvb7j/EfUvTqu1nar
dtBH1b52ItQ64dpfOb9EJxOKrlj5S9X3P+skg78jxnlhM3jw9nsUMxjEQkiRkcxQ
gxReyjtisdtNbquTLsqessW4Ae+h25zWWwXYHjwulKWdoV6qn1AmDkFisSWoojhn
imnOO0vYf3y3gAA7uMg0GkyL38BI59w/Wm4QzrzKFQqIk241O7GfCtMfnARqT5Wc
B7fiuKQ04dLbtLuJbXT7jxfKFhxJjZftvaITbpSqX8iHiEgLzp2Oi04FHwNCEYKL
f11BvXy5FN/0UMP5HVgm3ISTGXgIAOJzI/W+RlyJdOcOsM9ggK6j5oKxShuozvT1
dOGGJOHe1sj0nZB+EK3L9bDzEggqVYxndm5jsbF/hDi634ySYgZpNpUy9SXwPKTn
o5wQSlOUtDmL7gF7jAx4hEjLf3Ye+kIQ6fcsCkSBpukeK/+BX/nF6iFkVG5pIvUR
blSfB1Vh5K1ECAmQvcAIfm+ZpfG6vSN6pXDekEZ+ub42+9iGiujvY8jt9AtRp3Ey
DxzYCHXReDhj1bWPqvRGYMomNTANVJ09eAjhSj14cuXTtIgT8j0CAwEAAaAAMA0G
CSqGSIb3DQEBCwUAA4ICAQAFoWgBkVOazjVVf0nwwg7Sx0bIfRO7K4Q1X0L4tfWA
Kn+5cZqsJnZDJmHFztVQEdm6LmJAWqNb2qM/HQQPuQ2Or0bT8ZrEwgWCEOFFgN9o
X7KXMGIs+lvcSMQ3BKLT6IVbLKrciaFUXGmiPj4P/8DV3uPyK4WHfW0mcXipeJJF
4sUFg4+3EHEtL67bRI/SIxRlxyvegFJuHdw+ZQMVd8iUM9lWQLevlk9NiToMndgP
VLUK35f23Jz6ywOzHL0dYCtheGSrorrZlXho+zX6Fj3roXKrnTRJIT0YuVZJe4EN
1OX0pQJ+ES254WLgsNn9yZpLDbD1A8XA9nCSZMKPdcsLQE11mRQWAcS3/CTGWa1T
IvOdq3V3m6sLiNAfidTcqaa5Pvnhwd5K4UidmC7E1gFYwe8yJlY73NrrKDHLsV+l
Gr2LSiGxMuGI+Gzf2zypDXr8X7TCzfkcgnMIs/c1rJFIt0Q8E3zvj6q4jdf/fQzd
dqEp1p44zF7Fn5AEmdRNjgkvrJwipSFxK2eVfHU11m6W4MWWqUMN9AuRogFEV3N3
3OGOmWNzaf7APqvJ3uiTwJxO1J9u6a8G+TfLv8CK6hFlZGc1cow76Op71OjEx3Hv
ldCYID7xRZqYcUyhNn7NS0yN9RAfA3EznjNS8OW8R52DBo2x0LsPHDE8A1wJCSGr
EQ==
-----END CERTIFICATE REQUEST-----
- 对csr进行签名
openssl x509 -req -in server-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.conf
- -req 将传入证书申请
- -in 证书签名申请文件,server-req.pem
- -CA ca的证书文件ca-cert.pem
- -CAKey ca的私钥ca-key.pem
- -CAcreateserial 确保证书的签名带有唯一序列号
- -out 输出签名后的证书文件server-cert.pem
- extfile 可选项,告诉openssl我们有额外的配置,比如alternative name, email, IP address …
- 查看证书
openssl x509 -in server-cert.pem -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
17:6c:fb:d9:7f:60:9b:bd:16:26:86:94:ab:79:6b:94:ba:33:6c:7a
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = cn, ST = zhejiang, L = hangzhou, O = fightbee, OU = security, CN = *.fightbee.com, emailAddress = winsunxu@foxmail.com
Validity
Not Before: Mar 14 03:45:45 2023 GMT
Not After : Apr 13 03:45:45 2023 GMT
Subject: C = cn, ST = zhejiang, L = hangzhou, O = fightbee, OU = security, CN = *.fightbee.com, emailAddress = winsunxu@foxmail.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
Modulus:
00:b9:e3:29:fd:37:51:dd:da:e4:d1:5b:33:fc:fb:
...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:*.fightbee.com, DNS:*.fightbee.org, IP Address:0.0.0.0
X509v3 Subject Key Identifier:
0D:CA:70:EE:F6:AA:3B:D1:65:F7:DF:E1:02:9B:67:9C:83:4C:00:9F
X509v3 Authority Key Identifier:
EC:BB:86:67:BD:3A:02:8A:82:1C:D3:E2:59:DD:8E:9E:98:A9:39:C8
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
02:d6:b8:d7:0a:ab:a7:94:ce:b8:12:13:44:e6:40:8b:b5:07:
...
grpc server
server目录结构:
winsun@unbuntu64:~/test/grpc-tls-go$ tree -L 2
.
├── cert
│ ├── server-cert.pem
│ ├── server-ext.conf
│ ├── server-key.pem
|
├── go.mod
├── go.sum
├── proto
│ ├── helloworld_grpc.pb.go
│ ├── helloworld.pb.go
│ └── helloworld.proto
└── server.go
server 代码:
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net"
pb "grpctls/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
type greeterService struct {
pb.UnimplementedGreeterServer
}
func (s *greeterService) SayHello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("received name: %v", request.GetName())
return &pb.HelloReply{Message: "Hello " + request.GetName()}, nil
}
func main() {
// listen port
lis, err := net.Listen("tcp", "0.0.0.0:9000")
if err != nil {
log.Fatalf("list port err: %v", err)
}
// read ca's cert, verify to client's certificate
caPem, err := ioutil.ReadFile("cert/ca-cert.pem")
if err != nil {
log.Fatal(err)
}
// create cert pool and append ca's cert
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(caPem) {
log.Fatal(err)
}
// read server cert & key
serverCert, err := tls.LoadX509KeyPair("cert/server-cert.pem", "cert/server-key.pem")
if err != nil {
log.Fatal(err)
}
// configuration of the certificate what we want to
conf := &tls.Config{
Certificates: []tls.Certificate{serverCert},
//ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
}
//create tls certificate
tlsCredentials := credentials.NewTLS(conf)
// create grpc server
grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials))
// register service into grpc server
pb.RegisterGreeterServer(grpcServer, &greeterService{})
log.Printf("listening at %v", lis.Addr())
// listen port
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("grpc serve err: %v", err)
}
}
grpc client
client 目录结构:
(base) ➜ grpc-client tree -L 3
.
├── clien.go
├── go.mod
├── go.sum
├── proto
│ ├── helloworld.pb.go
│ ├── helloworld.proto
│ └── helloworld_grpc.pb.go
└── server-cert.pem
client主代码
package main
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "grpcclient/proto"
"log"
"time"
)
func main(){
//从输入的证书文件中为客户端构造TLS凭证
creds, err := credentials.NewClientTLSFromFile("server-cert.pem", "fightbee.com")
if err != nil {
log.Fatalf("Failed to create TLS credentials %v", err)
}
// 连接服务器
conn, err := grpc.Dial("192.168.44.133:9000", grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf("net.Connect err: %v", err)
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "winsun xu"})
if err != nil {
log.Fatal(err)
}
log.Printf("Greeting: %s", resp.GetMessage())
}
protoc 编译 .proto文件
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
参考
- 原文作者:winsun
- 原文链接:https://winsun.github.io/fightsec/post/go_14_clientserver_pattern/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。