前言
HTTP是明文传输的,即客户端与服务端之间通信的信息是可见的,这就存在被窃听、冒充或篡改的风险。HTTPS在HTTP和TCP之间加入了TLS协议,如图所示:
TLS协议主要解决了以下三个网络安全问题:
- 信息加密: HTTP 交互信息是被加密的,第三方就无法被窃取;
- 校验机制:校验信息传输过程中是否有被第三方篡改过,如果被篡改过,则会有警告提示;
- 身份证书:双方认证,双方都可以配置证书,防止身份被冒充;
客户端与服务端通过gRPC进行方法调用,也需要加入证书来保证调用的安全。
生成自签证书
安装openssl
# 下载openssl --- Win64 OpenSSL v3.0.5
https://slproweb.com/products/Win32OpenSSL.html
# 添加环境变量,将openssl.exe所在路径加入系统环境变量
C:\Program Files\OpenSSL-Win64\bin
生成私钥文件
openssl genrsa -des3 -out ca.key 2048
创建证书请求
openssl req -new -key ca.key -out ca.csr
生成ca.crt
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
修改openssl.cnf
# 打开copy_extensions
copy_extensions = copy
# 打开req_extensions
req_extensions = v3_req
# 找到[ v3_req ], 添加
subjectAltName = @alt_names
# 添加标签
[ alt_names ]
DNS.1 = *.dong.com
生成证书私钥
openssl genpkey -algorithm RSA -out server.key
通过私钥生成证书请求文件
openssl req -new -nodes -key server.key -out server.csr -days 3650 -config ./openssl.cnf -extensions v3_req
生成SAN证书
openssl x509 -req -days 365 -in server.csr -out server.pem -CA ca.crt -CAkey ca.key -CAc
reateserial -extfile ./openssl.cnf -extensions v3_req
说明:
- key:服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密;
- csr:证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名;
- crt:由颁发证书机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息;
- pem:是基于Base64编码的证书格式,扩展名包括PEM、CRT和CER;
单向认证
服务端修改
package main
import (
"service"
"google.golang.org/grpc"
"net"
"log"
"fmt"
"google.golang.org/grpc/credentials"
)
func main() {
fmt.Println("开始启动服务")
// 添加证书
creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
if err0 != nil {
log.Fatal("证书生成失败", err0)
}
// 创建rpc实例(添加认证)
rpcServer := grpc.NewServer(grpc.Creds(creds))
// 服务注册
service.RegisterProdServiceServer(rpcServer, service.ProductService)
// 启动监听
listener, err := net.Listen("tcp", ":8800")
if err != nil {
log.Fatal("启动监听失败", err)
}
// 启动服务
err = rpcServer.Serve(listener)
if err != nil {
log.Fatal("启动服务失败", err)
}
fmt.Println("启动服务成功")
}
服务端启动
客户端修改
package main
import (
"google.golang.org/grpc"
"log"
"service"
"context"
"fmt"
"google.golang.org/grpc/credentials"
)
func main() {
// 添加公钥
creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
if err0 != nil {
log.Fatal("证书错误: ", err0)
}
// 创建连接
conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatal("服务端连接失败: ", err)
}
fmt.Println("证书认证通过")
// 退出时关闭连接
defer conn.Close()
// 创建客户端实例
productServiceClient := service.NewProdServiceClient(conn)
// 方法请求
resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
if err != nil {
log.Fatal("调用gRPC方法失败: ", err)
}
fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}
客户端启动
单向认证流程
抓包分析
工程框架
双向认证
生成客户端公钥和私钥
# 生成私钥
openssl genpkey -algorithm RSA -out client.key
# 生成证书
openssl req -new -nodes -key client.key -out client.csr -days 3650 -config ./openssl.cnf -extensions v3_req
# 生成SAN证书
openssl x509 -req -days 365 -in client.csr -out client.pem -CA ca.crt -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
服务端修改
package main
import (
"service"
"google.golang.org/grpc"
"net"
"log"
"fmt"
"google.golang.org/grpc/credentials"
"crypto/tls"
"crypto/x509"
"io/ioutil"
)
func main() {
fmt.Println("开始启动服务")
// 添加证书
// creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
// if err0 != nil {
// log.Fatal("证书生成失败", err0)
// }
// 证书认证-双向认证
cert, err0 := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
if err0 != nil {
log.Fatal("证书读取失败", err0)
}
fmt.Println("证书读取成功")
// 创建一个新的、空的CertPool
certPool := x509.NewCertPool()
ca, err1 := ioutil.ReadFile("cert/ca.crt")
if err1 != nil {
log.Fatal("ca证书读取失败", err1)
}
fmt.Println("ca证书读取成功")
// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
certPool.AppendCertsFromPEM(ca)
// 构建基于TLS的TransportCredentials选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链, 允许包含一个或多个
Certificates: []tls.Certificate{cert},
// 要求必须校验客户端的证书
ClientAuth: tls.RequireAndVerifyClientCert,
// 设置根证书的集合, 校验方式使用ClientAuth中设定的模式
ClientCAs: certPool,
})
fmt.Println("设置TLS的TransportCredentials选项成功")
// 创建rpc实例
rpcServer := grpc.NewServer(grpc.Creds(creds))
// 服务注册
service.RegisterProdServiceServer(rpcServer, service.ProductService)
// 启动监听
listener, err := net.Listen("tcp", ":8800")
if err != nil {
log.Fatal("启动监听失败", err)
}
// 启动服务
err = rpcServer.Serve(listener)
if err != nil {
log.Fatal("启动服务失败", err)
}
fmt.Println("启动服务成功")
}
服务端启动
客户端修改
package main
import (
"google.golang.org/grpc"
"log"
"service"
"context"
"fmt"
"google.golang.org/grpc/credentials"
"crypto/tls"
"crypto/x509"
"io/ioutil"
)
func main() {
// 添加公钥
// creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
// if err0 != nil {
// log.Fatal("证书错误: ", err0)
// }
// 证书认证-双向认证
// 从证书相关文件中读取解析信息, 得到证书公钥、密钥对
cert, err0 := tls.LoadX509KeyPair("../cert/client.pem", "../cert/client.key")
if err0 != nil {
log.Fatal("证书读取失败", err0)
}
fmt.Println("证书读取成功")
// 创建一个新的、空的CertPool
certPool := x509.NewCertPool()
ca, err1 := ioutil.ReadFile("../cert/ca.crt")
if err1 != nil {
log.Fatal("ca证书读取失败", err1)
}
fmt.Println("ca证书读取成功")
// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
certPool.AppendCertsFromPEM(ca)
// 构建基于TLS的TransportCredentials选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链, 允许包含一个或多个
Certificates: []tls.Certificate{cert},
ServerName: "*.dong.com",
RootCAs: certPool,
})
fmt.Println("设置TLS的TransportCredentials选项成功")
// 创建连接
conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatal("服务端连接失败: ", err)
}
fmt.Println("证书认证通过")
// 退出时关闭连接
defer conn.Close()
// 创建客户端实例
productServiceClient := service.NewProdServiceClient(conn)
// 方法请求
resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
if err != nil {
log.Fatal("调用gRPC方法失败: ", err)
}
fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}
客户端启动
成功执行。
双向认证流程
抓包分析
工程框架
Token认证
服务端修改
package main
import (
"service"
"google.golang.org/grpc"
"net"
"log"
"fmt"
"google.golang.org/grpc/credentials"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"context"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/grpc/codes"
)
func main() {
fmt.Println("开始启动服务")
// 添加证书
// creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
// if err0 != nil {
// log.Fatal("证书生成失败", err0)
// }
// 证书认证-双向认证
cert, err0 := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
if err0 != nil {
log.Fatal("证书读取失败", err0)
}
fmt.Println("证书读取成功")
// 创建一个新的、空的CertPool
certPool := x509.NewCertPool()
ca, err1 := ioutil.ReadFile("cert/ca.crt")
if err1 != nil {
log.Fatal("ca证书读取失败", err1)
}
fmt.Println("ca证书读取成功")
// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
certPool.AppendCertsFromPEM(ca)
// 构建基于TLS的TransportCredentials选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链, 允许包含一个或多个
Certificates: []tls.Certificate{cert},
// 要求必须校验客户端的证书
ClientAuth: tls.RequireAndVerifyClientCert,
// 设置根证书的集合, 校验方式使用ClientAuth中设定的模式
ClientCAs: certPool,
})
fmt.Println("设置TLS的TransportCredentials选项成功")
// token认证 -- 合法的用户名和密码
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)
}
// 创建rpc实例
rpcServer := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(authInterceptor))
// 服务注册
service.RegisterProdServiceServer(rpcServer, service.ProductService)
// 启动监听
listener, err := net.Listen("tcp", ":8800")
if err != nil {
log.Fatal("启动监听失败", err)
}
// 启动服务
err = rpcServer.Serve(listener)
if err != nil {
log.Fatal("启动服务失败", err)
}
fmt.Println("启动服务成功")
}
func Auth(ctx context.Context) error {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return fmt.Errorf("missing credentials")
}
var user string
var passwd string
if val, ok := md["user"]; ok {
user = val[0]
}
if val, ok := md["passwd"]; ok {
passwd = val[0]
}
if user != "admin" || passwd != "admin@123" {
return status.Errorf(codes.Unauthenticated, "token认证失败")
}
fmt.Println("token认证成功")
return nil
}
添加认证接口
// 新增auth.go文件
package auth
import "context"
type Authentication struct {
User string
Passwd string
}
func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
map[string]string, error,
) {
return map[string]string{"user": a.User, "passwd": a.Passwd}, nil
}
func (a *Authentication) RequireTransportSecurity() bool {
return false
}
客户端修改
package main
import (
"google.golang.org/grpc"
"log"
"service"
"context"
"fmt"
"google.golang.org/grpc/credentials"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"auth"
)
func main() {
// 添加公钥
// creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
// if err0 != nil {
// log.Fatal("证书错误: ", err0)
// }
// 证书认证-双向认证
// 从证书相关文件中读取解析信息, 得到证书公钥、密钥对
cert, err0 := tls.LoadX509KeyPair("../cert/client.pem", "../cert/client.key")
if err0 != nil {
log.Fatal("证书读取失败", err0)
}
fmt.Println("证书读取成功")
// 创建一个新的、空的CertPool
certPool := x509.NewCertPool()
ca, err1 := ioutil.ReadFile("../cert/ca.crt")
if err1 != nil {
log.Fatal("ca证书读取失败", err1)
}
fmt.Println("ca证书读取成功")
// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
certPool.AppendCertsFromPEM(ca)
// 构建基于TLS的TransportCredentials选项
creds := credentials.NewTLS(&tls.Config{
// 设置证书链, 允许包含一个或多个
Certificates: []tls.Certificate{cert},
ServerName: "*.dong.com",
RootCAs: certPool,
})
fmt.Println("设置TLS的TransportCredentials选项成功")
token := &auth.Authentication{
User: "admin",
Passwd: "admin@123",
}
// 创建连接
conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(token))
if err != nil {
log.Fatal("服务端连接失败: ", err)
}
fmt.Println("证书认证通过")
// 退出时关闭连接
defer conn.Close()
// 创建客户端实例
productServiceClient := service.NewProdServiceClient(conn)
// 方法请求
resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
if err != nil {
log.Fatal("调用gRPC方法失败: ", err)
}
fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}
测试
1)先启动服务端,再启动服务端
服务端
客户端
token认证通过。
工程框架
部分参考:
HTTPS协议:https://blog.csdn.net/qq_34827674/article/details/112589634
数字签名和数字证书:https://blog.csdn.net/qq_34827674/article/details/119081396文章来源:https://www.toymoban.com/news/detail-422171.html
单向认证与双向认证:https://blog.csdn.net/qq_53267860/article/details/125045094文章来源地址https://www.toymoban.com/news/detail-422171.html
到了这里,关于grpc介绍(二)——认证方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!