gRPC SSL/TLS双向认证--SSL/TLS 工作原理

一、SSL/TLS 介绍

什么是 SSL, 什么是 TLS 呢?官话说 SSL 是安全套接层 (secure sockets layer), TLS 是 SSL 的继任者,叫传输层安全 (transport layer security)。说白点,就是在明文的上层和 TCP 层之间加上一层加密,这样就保证上层信息传输的安全。如HTTP 协议是明文传输,加上 SSL 层之后,就有了雅称 HTTPS。它存在的唯一目的就是保证上层通讯安全的一套机制。它的发展依次经历了下面几个时期,像手机软件升级一样,每次更新都添加或去除功能,比如引进新的加密算法,修改握手方式等。

SSL1.0: 已废除
SSL2.0: RFC6176, 已废除
SSL3.0: RFC6101, 基本废除
TLS1.0: RFC2246, 少数古董服务器仍在使用
TLS1.1: RFC4346
TLS1.2: RFC5246, 目前已广泛使用
TLS1.3: RFC8446


二、SSL/TLS 单向认证流程


三、SSL/TLS 双向认证流程


四、gRPC连接类型

  • 第一个是不安全的连接,我们从本课程开始就一直在使用它。在这方面,客户端和服务器之间传输的所有数据都未加密。所以请不要在生产中使用它!
  • 第二种类型是由服务器端 TLS 保护的连接。在这种情况下,所有数据都被加密,但只有服务器需要向客户端提供其 TLS 证书。如果服务器不关心哪个客户端正在调用其 API,则可以使用这种类型的连接。
  • 第三种也是最强的类型是由双向 TLS 保护的连接。当服务器还需要验证谁在调用它的服务时,我们会使用它。所以在这种情况下,客户端和服务器都必须向对方提供他们的 TLS 证书。

五、gRPC 双向认证

1、TLS证书生成

生成CA的私钥和自签名证书

openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout ca-key.pem -out ca-cert.pem -subj "/C=CN/ST=SC/L=ChenDu/O=AISHU/OU=Database/CN=*.aishu.cn/emailAddress=xxxxxx.xxx@gmail.com"

生成服务器的私钥和证书签名请求(CSR)

openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=CN/ST=SC/L=ChenDu/O=AISHU/OU=Database/CN=localhost/emailAddress=xxxxxx.xxx@gmail.com"

使用CA的私钥对服务器的CSR进行签名,并取回已签名证书

openssl x509 -req -in server-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem

生成客户端的私钥和证书签名请求(CSR)

openssl req -newkey rsa:4096 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=CN/ST=SC/L=ChenDu/O=AISHU/OU=Database/CN=localhost/emailAddress=xxxxxx.xxx@gmail.com"

使用CA的私钥对服务器的CSR进行签名,并取回已签名证书

openssl x509 -req -in client-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem

CA 校验证书测试

openssl verify -CAfile ca-cert.pem server-cert.pem
openssl verify -CAfile ca-cert.pem client-cert.pem

注意:生产环境中需要将 CN=localhost 替换成实际域名

2、gRPC 服务端实现

#include <iostream>
#include <memory>
#include <string>

#include <grpc++/grpc++.h>
#include <grpc++/security/credentials.h>
#include <fstream>

#include "hello.grpc.pb.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloResponse;
using helloworld::Greeter;

static std::string get_file_contents(const char *fpath)
{
  std::ifstream finstream(fpath);
  std::string contents((std::istreambuf_iterator<char>(finstream)),
                       std::istreambuf_iterator<char>());
  return contents;
}

// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloResponse* reply) override {
    std::string prefix("Hello ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
};

void RunServer(char** argv) {
  std::string server_address("127.0.0.1:50051");
  GreeterServiceImpl service;

  auto client_ca_pem = get_file_contents(argv[1]); // for verifying clients
  auto my_key_pem = get_file_contents(argv[2]);
  auto my_cert_pem = get_file_contents(argv[3]);

  grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {
    my_key_pem.c_str(), my_cert_pem.c_str()
  };

  grpc::SslServerCredentialsOptions ssl_opts(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
  ssl_opts.pem_root_certs = client_ca_pem;
  ssl_opts.pem_key_cert_pairs.push_back(pkcp);
  std::cout << "pem_root_certs length: " << ssl_opts.pem_root_certs.length() << std::endl;

  std::shared_ptr<grpc::ServerCredentials> creds = grpc::SslServerCredentials(ssl_opts);

  ServerBuilder builder;
  // Listen on the given address without any authentication mechanism.
  builder.AddListeningPort(server_address, creds);
  // Register "service" as the instance through which we'll communicate with
  // clients. In this case it corresponds to an *synchronous* service.
  builder.RegisterService(&service);
  // Finally assemble the server.
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;

  // Wait for the server to shutdown. Note that some other thread must be
  // responsible for shutting down the server for this call to ever return.
  server->Wait();
}

int main(int argc, char** argv) {
  RunServer(argv);

  return 0;
}

3、gRPC 客户端实现

#include <iostream>
#include <memory>
#include <string>

#include <grpc++/grpc++.h>
#include <grpc++/security/credentials.h>
#include <fstream>

#include "hello.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloResponse;
using helloworld::Greeter;

static std::string get_file_contents(const char *fpath)
{
  std::ifstream finstream(fpath);
  std::string contents((std::istreambuf_iterator<char>(finstream)),
                       std::istreambuf_iterator<char>());
  return contents;
}

class GreeterClient {
 public:
  GreeterClient(std::shared_ptr<Channel> channel)
      : stub_(Greeter::NewStub(channel)) {}

  // Assembles the client's payload, sends it and presents the response back
  // from the server.
  std::string SayHello(const std::string& user) {
    // Data we are sending to the server.
    HelloRequest request;
    request.set_name(user);

    // Container for the data we expect from the server.
    HelloResponse reply;

    // Context for the client. It could be used to convey extra information to
    // the server and/or tweak certain RPC behaviors.
    ClientContext context;

    // The actual RPC.
    Status status = stub_->SayHello(&context, request, &reply);

    // Act upon its status.
    if (status.ok()) {
      return reply.message();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      return "RPC failed";
    }
  }

 private:
  std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char** argv) {
  // Instantiate the client. It requires a channel, out of which the actual RPCs
  // are created. This channel models a connection to an endpoint (in this case,
  // localhost at port 50051). We indicate that the channel isn't authenticated
  // (use of InsecureChannelCredentials()).

  assert (argc == 4);
  auto server_ca_pem = get_file_contents(argv[1]);
  auto my_key_pem = get_file_contents(argv[2]);
  auto my_cert_pem = get_file_contents(argv[3]);

  grpc::SslCredentialsOptions ssl_opts;
  ssl_opts.pem_root_certs = server_ca_pem;
  ssl_opts.pem_private_key = my_key_pem;
  ssl_opts.pem_cert_chain = my_cert_pem;

  std::shared_ptr<grpc::ChannelCredentials> creds = grpc::SslCredentials(ssl_opts);
    grpc::ChannelArguments channel_args;
    channel_args.SetSslTargetNameOverride("foo.test.google.fr");

    GreeterClient greeter(grpc::CreateCustomChannel("localhost:50051", creds, channel_args));

//   GreeterClient greeter(grpc::CreateChannel(
//       "localhost:50051", creds));
  std::string user("world");
  std::string reply = greeter.SayHello(user);
  std::cout << "Greeter received: " << reply << std::endl;

  return 0;
}

4、运行截图

服务端截图

客户端截图

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
gRPC是一种高性能、开源的远程过程调用(RPC)框架,它使用Protocol Buffers作为接口定义语言(IDL)。双向认证是指在进行gRPC通信时,服务端和客户端都需要进行身份验证。在gRPC中,双向认证可以通过使用TLS/SSL证书来实现。 在gRPC中实现双向认证需要以下步骤: 1. 生成证书:首先,需要生成服务端和客户端的证书。可以使用openssl或其他工具来生成证书和私钥。 2. 配置TLS/SSL:服务端和客户端都需要配置TLS/SSL来启用加密通信。服务端需要加载证书和私钥,而客户端需要加载服务端的证书用于验证服务端身份。 3. 配置双向认证:服务端和客户端都需要配置双向认证。服务端需要验证客户端的证书,而客户端需要验证服务端的证书。 4. 实现认证逻辑:在服务端和客户端的代码中,需要实现证书验证逻辑。可以使用TLS配置中的回调函数来进行验证。 可以参考引用\[2\]中提供的gRPC-Gateway和引用\[3\]中的依赖安装命令来实现gRPC双向认证。这些工具和库可以帮助简化双向认证的实现过程。 总结起来,golang中实现gRPC双向认证的步骤包括生成证书、配置TLS/SSL、配置双向认证和实现认证逻辑。通过这些步骤,可以确保服务端和客户端之间的通信是安全和可信的。 #### 引用[.reference_title] - *1* [Go Grpc Jwt身份认证和Gateway集成以及HTTPS双向认证](https://blog.csdn.net/dz45693/article/details/112180692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【gRPC双向认证grpc-gateway原理及简单使用](https://blog.csdn.net/dl962454/article/details/124384299)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值