一、grpc SSL源码分析

官方文档说明

gRPC – Authentication
gRPC 官方文档中文版_V1.0

由于车载终端开发语言为C++,大致查阅了一下官方相关的文档,文档描述的内容比较简单。

服务端认证加密使用的 SSL/TLS
这是个最简单的认证场景:一个客户端仅仅想认证服务器并且加密所有数据。

1.客户端处理流程

// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel(server_name, creds);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);

对于高级的用例比如改变根 CA 或使用客户端证书,可以在发送给工厂方法的 SslCredentialsOptions 参数里的相应选项进行设置。

查看了一下源码大致实现的过程。

(1)配置参数

SslCredentials方法的作用是根据SSL特定选项构建SSL证书。在grpc\include\grpcpp\security\credentials_impl.h中定义。

/// Builds SSL Credentials given SSL specific options
std::shared_ptr<ChannelCredentials> SslCredentials(const SslCredentialsOptions& options);

具体实现在grpc\src\cpp\client\secure_credentials.cc中实现。

// Builds SSL Credentials given SSL specific options
std::shared_ptr<ChannelCredentials> SslCredentials(const SslCredentialsOptions& options) {grpc::GrpcLibraryCodegen init;  // To call grpc_init().grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};grpc_channel_credentials* c_creds = grpc_ssl_credentials_create(options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr,nullptr);return WrapChannelCredentials(c_creds);
}

传递进来的option参数

/// Options used to build SslCredentials.
struct SslCredentialsOptions {/// The buffer containing the PEM encoding of the server root certificates. If/// this parameter is empty, the default roots will be used.  The default/// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH/// environment variable pointing to a file on the file system containing the/// roots.grpc::string pem_root_certs;/// The buffer containing the PEM encoding of the client's private key. This/// parameter can be empty if the client does not have a private key.grpc::string pem_private_key;/// The buffer containing the PEM encoding of the client's certificate chain./// This parameter can be empty if the client does not have a certificate/// chain.grpc::string pem_cert_chain;
};

pem_root_certs根证书,如果参数为空,它会使用默认的根证书。同时环境变量GRPC_DEFAULT_SSL_ROOTS_FILE_PATH,可以设置grpc搜索根证书的路径。

我实际测试如果该参数为空的话会出现握手失败的错误。

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_client
I0423 15:23:48.876649338   10329 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 15:23:48.876739676   10329 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
E0423 15:23:48.934196140   10329 ssl_transport_security.cc:1379] Handshake failed with fatal error SSL_ERROR_SSL: error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED.
I0423 15:23:48.934377487   10329 subchannel.cc:1003]         Connect failed: {"created":"@1587626628.934217529","description":"Handshake failed","file":"/home/workspace/project/grpc_project/grpc/src/core/lib/security/transport/security_handshaker.cc","file_line":307,"tsi_code":10,"tsi_error":"TSI_PROTOCOL_FAILURE"}
I0423 15:23:48.934517994   10329 subchannel.cc:942]          Subchannel 0x14dabd0: Retry in 997 milliseconds
14: failed to connect to all addresses
Greeter received: RPC failed

因此我在客户端上的pem_root_certs根证书设置为服务端的自签名证书,服务端上的pem_root_certs根证书设置为客户端的自签名证书。有个疑惑是SSL/TSL双向认证的时候会请求对方发生证书过来进行验证。grpc这个操作是否为了内部直接根据传递进来的证书自行验证从而省去请求的动作呢?

因此客户端和服务端由不同CA签名,服务端导入客户端的CA,客户端导入服务器CA。这时候可通过设置客户端pem_root_certs为serverCA.crt 服务端pem_root_certs设置为clientCA.crt来进行身份验证。

参数pem_cert_chain证书链,如果没有可以为空。但是我实际测试pkcp.cert_chain = “”;运行服务端会报错。我个人理解为pem_cert_chain充当公钥的作用,因此需要和pem_private_key成对出现。如果使用自签名证书的话,这个参数直接传递客户端自签名证书。

E0423 15:34:01.190980112   10763 ssl_transport_security.cc:777] Invalid cert chain file.
E0423 15:34:01.191044404   10763 ssl_security_connector.cc:263] Handshaker factory creation failed with TSI_INVALID_ARGUMENT.
/** Object that holds a private key / certificate chain pair in PEM format. */
typedef struct {/** private_key is the NULL-terminated string containing the PEM encoding ofthe client's private key. */const char* private_key;/** cert_chain is the NULL-terminated string containing the PEM encoding ofthe client's certificate chain. */const char* cert_chain;
} grpc_ssl_pem_key_cert_pair;

私钥证书对。
options.pem_private_key.c_str()为取字符串的首地址。
string.c_str是Borland封装的String类中的一个函数,它返回当前字符串的首字符地址。

(2)生成grpc ssl证书

将openssl生成的私钥证书通过接口生成grpc证书。

/** Deprecated in favor of grpc_ssl_server_credentials_create_ex. It will beremoved after all of its call sites are migrated togrpc_ssl_server_credentials_create_ex. Creates an SSL credentials object.The security level of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY.- pem_root_certs is the NULL-terminated string containing the PEM encodingof the server root certificates. If this parameter is NULL, theimplementation will first try to dereference the file pointed by theGRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable, and if that fails,try to get the roots set by grpc_override_ssl_default_roots. Eventually,if all these fail, it will try to get the roots from a well-known place ondisk (in the grpc install directory).gRPC has implemented root cache if the underlying OpenSSL library supportsit. The gRPC root certificates cache is only applicable on the defaultroot certificates, which is used when this parameter is nullptr. If userprovides their own pem_root_certs, when creating an SSL credential object,gRPC would not be able to cache it, and each subchannel will generate acopy of the root store. So it is recommended to avoid providing large roompem with pem_root_certs parameter to avoid excessive memory consumption,particularly on mobile platforms such as iOS.- pem_key_cert_pair is a pointer on the object containing client's privatekey and certificate chain. This parameter can be NULL if the client doesnot have such a key/cert pair.- verify_options is an optional verify_peer_options object which holdsadditional options controlling how peer certificates are verified. Forexample, you can supply a callback which receives the peer's certificatewith which you can do additional verification. Can be NULL, in whichcase verification will retain default behavior. Any settings inverify_options are copied during this call, so the verify_optionsobject can be released afterwards. */
GRPCAPI grpc_channel_credentials* grpc_ssl_credentials_create(const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,const verify_peer_options* verify_options, void* reserved);

grpc_ssl_credentials_create参数说明:
pem_root_certs:如果为空,grpc会从环境变量GRPC_DEFAULT_SSL_ROOTS_FILE_PATH中搜索是否有根证书。如果失败,会尝试通过grpc_override_ssl_default_roots设置根。如果还是失败就会从grpc的安装目录中查找。这块比较容易理解,但是后面一段有个根缓存的东西,后面提到一点如果使用自己提供的根证书,提到一点避免large room pem 我个人的理解是密钥的长度不宜过大,但我觉得一般2048位也不大啊,不至于消耗过多的内存。这一点会影响到性能,还是注意一下,后续找找资料看看。

pem_key_cert_pair 是对象上的一个指针,该对象包含客户机的私钥和证书链。如果客户端没有这样的密钥/证书对,则此参数可以为空。这部分内容应该涉及SSL单向认证还是双向认证的问题。

verify_options是一个可选的verify_peer_options对象,它包含控制对等证书验证方式的其他选项。例如,您可以提供一个回调,它接收对等方的证书,您可以使用它进行额外的验证。可以为NULL,在这种情况下,验证将保留默认行为。在调用期间复制verify_options中的任何设置,因此可以在调用后释放verify_options对象。
这个参数暂时也不关心,实际都设置为NULL。

(3)创建Channel

使用域名和证书创建一个通讯的频道。
CreateChannel实现

grpc_channel* CreateChannel(const char* target, const grpc_channel_args* args) {if (target == nullptr) {gpr_log(GPR_ERROR, "cannot create channel with NULL target name");return nullptr;}// Add channel arg containing the server URI.grpc_core::UniquePtr<char> canonical_target =ResolverRegistry::AddDefaultPrefixIfNeeded(target);grpc_arg arg = grpc_channel_arg_string_create(const_cast<char*>(GRPC_ARG_SERVER_URI), canonical_target.get());const char* to_remove[] = {GRPC_ARG_SERVER_URI};grpc_channel_args* new_args =grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1);grpc_channel* channel =grpc_channel_create(target, new_args, GRPC_CLIENT_CHANNEL, nullptr);grpc_channel_args_destroy(new_args);return channel;
}

有点复杂,暂时不用这么深究。大致搞清楚两个参数的用法即可。target传入域名端口,args传入证书即可。

(4)创建存根并进行RPC远程调用
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);

2.服务端处理流程

(1)创建证书

没找到服务端的处理流程,去github上找到一个大神写的demo。

empty root CA certificate causes C++ server to incorrectly accept clients · Issue #12146 · grpc/grpc

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::shared_ptr<grpc::ServerCredentials> creds = grpc::SslServerCredentials(ssl_opts);ServerBuilder builder;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;

SslServerCredentialsOptions配置服务端的SSL密钥证书这部分与客户端差不多。

/// Options to create ServerCredentials with SSL
struct SslServerCredentialsOptions {/// \warning DeprecatedSslServerCredentialsOptions(): force_client_auth(false),client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}SslServerCredentialsOptions(grpc_ssl_client_certificate_request_type request_type): force_client_auth(false), client_certificate_request(request_type) {}struct PemKeyCertPair {grpc::string private_key;grpc::string cert_chain;};grpc::string pem_root_certs;std::vector<PemKeyCertPair> pem_key_cert_pairs;/// \warning Deprecatedbool force_client_auth;/// If both \a force_client_auth and \a client_certificate_request/// fields are set, \a force_client_auth takes effect, i.e./// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY/// will be enforced.grpc_ssl_client_certificate_request_type client_certificate_request;
};

grpc_ssl_client_certificate_request_type 枚举类型在include\grpc\grpc_security_constants.h中定义。

typedef enum {/** Server does not request client certificate.The certificate presented by the client is not checked by the server atall. (A client may present a self signed or signed certificate or notpresent a certificate at all and any of those option would be accepted) */GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,/** Server requests client certificate but does not enforce that the clientpresents a certificate.If the client presents a certificate, the client authentication is left tothe application (the necessary metadata will be available to theapplication via authentication context properties, see grpc_auth_context).The client's key certificate pair must be valid for the SSL connection tobe established. */GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,/** Server requests client certificate but does not enforce that the clientpresents a certificate.If the client presents a certificate, the client authentication is done bythe gRPC framework. (For a successful connection the client needs to eitherpresent a certificate that can be verified against the root certificateconfigured by the server or not present a certificate at all)The client's key certificate pair must be valid for the SSL connection tobe established. */GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,/** Server requests client certificate and enforces that the client presents acertificate.If the client presents a certificate, the client authentication is left tothe application (the necessary metadata will be available to theapplication via authentication context properties, see grpc_auth_context).The client's key certificate pair must be valid for the SSL connection tobe established. */GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,/** Server requests client certificate and enforces that the client presents acertificate.The certificate presented by the client is verified by the gRPC framework.(For a successful connection the client needs to present a certificate thatcan be verified against the root certificate configured by the server)The client's key certificate pair must be valid for the SSL connection tobe established. */GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
} grpc_ssl_client_certificate_request_type;

因为需要双向认证,因此参数设置为GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY(GRPC SSL请求和要求客户端证书和验证)

SslServerCredentials创建证书
一直很纳闷为啥客户端的接口不声明成SslClientCredentials而是SslCredentials。不过影响不大,使用接口的时候注意一下就好。

具体实现grpc\src\cpp\server\secure_server_credentials.cc

std::shared_ptr<ServerCredentials> SslServerCredentials(const grpc::SslServerCredentialsOptions& options) {std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;for (const auto& key_cert_pair : options.pem_key_cert_pairs) {grpc_ssl_pem_key_cert_pair p = {key_cert_pair.private_key.c_str(),key_cert_pair.cert_chain.c_str()};pem_key_cert_pairs.push_back(p);}grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create_ex(options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0],pem_key_cert_pairs.size(),options.force_client_auth? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: options.client_certificate_request,nullptr);return std::shared_ptr<ServerCredentials>(new SecureServerCredentials(c_creds));
}
(2)创建服务器流程
ServerBuilder builder;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;

与普通socket建立大同小异。
grpc\include\grpcpp中server_builder_impl.h 定义ServerBuilder类

 /// Return a running server which is ready for processing calls./// Before calling, one typically needs to ensure that:///  1. a service is registered - so that the server knows what to serve///     (via RegisterService, or RegisterAsyncGenericService)///  2. a listening port has been added - so the server knows where to receive///     traffic (via AddListeningPort)///  3. [for async api only] completion queues have been added via///     AddCompletionQueuevirtual std::unique_ptr<grpc::Server> BuildAndStart();/// Register a service. This call does not take ownership of the service./// The service must exist for the lifetime of the \a Server instance returned/// by \a BuildAndStart()./// Matches requests with any :authorityServerBuilder& RegisterService(grpc::Service* service);/// Enlists an endpoint \a addr (port with an optional IP address) to/// bind the \a grpc::Server object to be created to.////// It can be invoked multiple times.////// \param addr_uri The address to try to bind to the server in URI form. If/// the scheme name is omitted, "dns:///" is assumed. To bind to any address,/// please use IPv6 any, i.e., [::]:<port>, which also accepts IPv4/// connections.  Valid values include dns:///localhost:1234, //// 192.168.1.1:31416, dns:///[::1]:27182, etc.)./// \param creds The credentials associated with the server./// \param selected_port[out] If not `nullptr`, gets populated with the port/// number bound to the \a grpc::Server for the corresponding endpoint after/// it is successfully bound by BuildAndStart(), 0 otherwise. AddListeningPort/// does not modify this pointer.ServerBuilder& AddListeningPort(const grpc::string& addr_uri,std::shared_ptr<grpc_impl::ServerCredentials> creds,int* selected_port = nullptr);

创建一个ServerBuilder对象,AddListeningPort这个方法将域名端口绑定服务器,证书与服务器进行关联。
RegisterService注册服务器
BuildAndStart接口创建并运行服务器。

简单流程

  • 创建服务对象
  • 创建构造服务器对象
  • 绑定服务器ip和端口
  • 注册服务器
  • 创建并运行服务器

二、简单实例

1.客户端代码

#include <iostream>
#include <memory>
#include <string>#include <grpc++/grpc++.h>
#include <grpc++/security/credentials.h>
#include <fstream>#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endifusing grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;const char servercert_path[] = "./ssl_key/server_self_signed_crt.pem";
const char clientcert_path[] = "./ssl_key/client_self_signed_crt.pem";
const char clientkey_path[]  = "./ssl_key/client_privatekey.pem";static std::string get_file_contents(const char *fpath)
{std::ifstream finstream(fpath);std::string contents;contents.assign((std::istreambuf_iterator<char>(finstream)),std::istreambuf_iterator<char>());finstream.close();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.HelloReply 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()).auto servercert = get_file_contents(servercert_path);auto clientkey  = get_file_contents(clientkey_path);auto clientcert = get_file_contents(clientcert_path);grpc::SslCredentialsOptions ssl_opts;ssl_opts.pem_root_certs  = servercert;ssl_opts.pem_private_key = clientkey;ssl_opts.pem_cert_chain  = clientcert;std::shared_ptr<grpc::ChannelCredentials> creds = grpc::SslCredentials(ssl_opts);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;
}

2.服务端代码

#include <iostream>
#include <memory>
#include <string>#include <grpc++/grpc++.h>
#include <grpc++/security/credentials.h>
#include <fstream>#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endifusing grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;const char clientcert_path[] = "./ssl_key/client_self_signed_crt.pem";
const char servercert_path[] = "./ssl_key/server_self_signed_crt.pem";
const char serverkey_path[]  = "./ssl_key/server_privatekey.pem";static std::string get_file_contents(const char *fpath)
{std::ifstream finstream(fpath);std::string contents;contents.assign((std::istreambuf_iterator<char>(finstream)),std::istreambuf_iterator<char>());finstream.close();return contents;
}// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override {std::string prefix("Hello ");reply->set_message(prefix + request->name());return Status::OK;}
};void RunServer(char** argv) {std::string server_address("localhost:50051");GreeterServiceImpl service;auto clientcert = get_file_contents(clientcert_path); // for verifying clientsauto servercert = get_file_contents(servercert_path);auto serverkey  = get_file_contents(serverkey_path);grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {serverkey.c_str(), servercert.c_str()};grpc::SslServerCredentialsOptions ssl_opts(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);ssl_opts.pem_root_certs = clientcert;ssl_opts.pem_key_cert_pairs.push_back(pkcp);std::shared_ptr<grpc::ServerCredentials> creds;creds = grpc::SslServerCredentials(ssl_opts);ServerBuilder builder;builder.AddListeningPort(server_address, creds);builder.RegisterService(&service);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.密钥生成脚本

#!/bin/shecho Generate CA key:
openssl genrsa -passout pass:1111 -des3 -out ca.key 2048echo Generate CA certificate:
openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"echo Generate server key:
openssl genrsa -passout pass:1111 -des3 -out server_privatekey.pem 2048echo Generate server signing request:
openssl req -passin pass:1111 -new -key server_privatekey.pem -out server_csr.pem -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"echo Self-sign server certificate:
openssl x509 -req -passin pass:1111 -days 3650 -in server_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out server_self_signed_crt.pemecho Remove passphrase from server key:
openssl rsa -passin pass:1111 -in server_privatekey.pem -out server_privatekey.pemecho Generate client key
openssl genrsa -passout pass:1111 -des3 -out client_privatekey.pem 2048echo Generate client signing request:
openssl req -passin pass:1111 -new -key client_privatekey.pem -out client_csr.pem -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"echo Self-sign client certificate:
openssl x509 -passin pass:1111 -req -days 3650 -in client_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out client_self_signed_crt.pemecho Remove passphrase from client key:
openssl rsa -passin pass:1111 -in client_privatekey.pem -out client_privatekey.pem

4.实际测试运行结果

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_server
I0423 14:38:07.332275206    8702 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:38:07.332379098    8702 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:38:07.333027275    8702 server_builder.cc:332]      Synchronous server. Num CQs: 1, Min pollers: 1, Max Pollers: 2, CQ timeout (msec): 10000
Server listening on localhost:50051root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_client
I0423 14:38:14.484686548    8709 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:38:14.484789046    8709 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:38:14.504947090    8709 subchannel.cc:1055]         New connected subchannel at 0xd5d5f0 for subchannel 0xd5cd70
Greeter received: Hello world

三、注意事项

1.如果grpc SSL加密传输绑定为IP,并且证书不包含域名信息。
(生成密钥证书脚本中的-subj CN为设置域名)

GreeterClient greeter(grpc::CreateChannel("127.0.0.1:50051", creds));

会出现如下错误

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_server
I0423 14:33:47.782953499    8547 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:33:47.783058492    8547 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:33:47.783750228    8547 server_builder.cc:332]      Synchronous server. Num CQs: 1, Min pollers: 1, Max Pollers: 2, CQ timeout (msec): 10000
I0423 14:33:47.788131754    8547 ssl_transport_security.cc:279] Could not get common name of subject from certificate.
Server listening on 127.0.0.1:50051
E0423 14:33:56.277264861    8551 ssl_transport_security.cc:1709] No match found for server name: 127.0.0.1.
I0423 14:33:56.283370850    8551 ssl_transport_security.cc:279] Could not get common name of subject from certificate.root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_client
I0423 14:33:56.270917152    8553 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:33:56.271035505    8553 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:33:56.283508687    8553 ssl_transport_security.cc:279] Could not get common name of subject from certificate.
I0423 14:33:56.283885220    8553 subchannel.cc:1003]         Connect failed: {"created":"@1587623636.283787741","description":"Peer name 127.0.0.1 is not in peer certificate","file":"/home/workspace/project/grpc_project/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc","file_line":55}
I0423 14:33:56.284013256    8553 subchannel.cc:942]          Subchannel 0xe1f850: Retry in 988 milliseconds
14: failed to connect to all addresses
Greeter received: RPC failed

2.如果grpc SSL加密传输绑定为域名,并且证书不包含域名信息。
比如我实际测试未添加/CN=localhost。即使程序中使用

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

运行依然会出错

E0423 14:36:02.849671123    8657 ssl_transport_security.cc:1709] No match found for server name: localhost.
I0423 14:36:02.866499334    8657 ssl_transport_security.cc:279] Could not get common name of subject from certifica

3.使用自定义域名
如果没有进行域名与ip绑定会出错

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_server
I0423 14:51:20.496356740    9318 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:51:20.496452505    9318 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:51:20.497141392    9318 server_builder.cc:332]      Synchronous server. Num CQs: 1, Min pollers: 1, Max Pollers: 2, CQ timeout (msec): 10000
E0423 14:51:26.347566527    9318 server_secure_chttp2.cc:81] {"created":"@1587624686.347327002","description":"Name or service not known","errno":-2,"file":"/home/workspace/project/grpc_project/grpc/src/core/lib/iomgr/resolve_address_posix.cc","file_line":108,"os_error":"Name or service not known","syscall":"getaddrinfo","target_address":"www.chenwr2018.com:50051"}
Server listening on www.chenwr2018.com:50051
Segmentation fault (core dumped)

IP绑定域名。
vi /etc/hosts 修改内容如图所示。

GreeterClient greeter(grpc::CreateChannel("www.chenwr2018.com:50051", creds)); 端口可加也可不加。

运行结果:

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_server
I0423 14:55:26.994279385    9562 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:55:26.994376646    9562 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:55:26.995203388    9562 server_builder.cc:332]      Synchronous server. Num CQs: 1, Min pollers: 1, Max Pollers: 2, CQ timeout (msec): 10000
Server listening on www.chenwr2018.com:50051root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_client
I0423 14:57:56.877750911    9610 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 14:57:56.877857866    9610 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 14:57:56.897584852    9610 subchannel.cc:1055]         New connected subchannel at 0x148f5e0 for subchannel 0x147dd30
Greeter received: Hello world

4.如果使用不相关的证书
报错

root@chenwr-pc:/home/workspace/project/grpc_project/grpc# GRPC_VERBOSITY=INFO ./greeter_server
I0423 15:00:59.280097209    9847 ev_epollex_linux.cc:1631]   Skipping epollex because it is not supported.
I0423 15:00:59.280191481    9847 ev_epoll1_linux.cc:116]     grpc epoll fd: 3
I0423 15:00:59.280828605    9847 server_builder.cc:332]      Synchronous server. Num CQs: 1, Min pollers: 1, Max Pollers: 2, CQ timeout (msec): 10000
E0423 15:00:59.284965656    9847 ssl_transport_security.cc:785] Invalid private key.
E0423 15:00:59.285014702    9847 ssl_security_connector.cc:263] Handshaker factory creation failed with TSI_INVALID_ARGUMENT.
E0423 15:00:59.285068617    9847 server_secure_chttp2.cc:81] {"created":"@1587625259.285041668","description":"Unable to create secure server with credentials of type Ssl.","file":"/home/workspace/project/grpc_project/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc","file_line":63}
Server listening on www.chenwr2018.com:50051
Segmentation fault (core dumped)

gRPC SSL加密传输数据实例(C++版)相关推荐

  1. SSL应用系列之二:为Web站点实现SSL加密访问

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://jeffyyko.blog.51cto.com/28563/141322 上一节中 ...

  2. SSL加密与分布式IM系统-InChat1.1.3版本试用说明

    本文首发于本博客 猫叔的博客,转载请申明出处 2019年1月15号-InChat发布V1.1.3版本 InChat 一个轻量级.高效率的支持多端(应用与硬件Iot)的异步网络应用通讯框架 v1.1.3 ...

  3. 连接数据库报com.microsoft.sqlserver.jdbc.SQLServerException: 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接

    JDBC加载驱动,连接SQLServer 2012 报 java.ext.dirs: C:\Program Files\Java\jdk1.8.0_331\jre\lib\ext;C:\Windows ...

  4. ftp连接oracle服务器,使用SSL加密连接FTP - 架建SSL安全加密的FTP服务器(图)_服务器应用_Linux公社-Linux系统门户网站...

    四.使用SSL加密连接FTP 启用Serv-U服务器的SSL功能后,就可以利用此功能安全传输数据了,但FTP客户端程序必须支持SSL功能才行. 如果我们直接使用IE浏览器进行登录则会出现图4显示的错误 ...

  5. MySQL(MariaDB)的 SSL 加密复制

    背景: 在默认的主从复制过程或远程连接到MySQL/MariaDB所有的链接通信中的数据都是明文的,在局域网内连接倒问题不大:要是在外网里访问数据或则复制,则安全隐患会被放大很多.由于项目要求需要直接 ...

  6. MySQL连接、SSL加密与密码插件

    博客说明: 仅仅是作为个人学习的一种记录和补充,仅供参考.欢迎指正和共同进步 学习视频原网址: https://www.bilibili.com/video/BV1J5411A7ei?spm_id_f ...

  7. AES与RSA混合加密完整实例

    前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用RSA来 ...

  8. JDBC连接数据库遇到的“驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。”问题解决方法

    JDBC连接数据库遇到的"驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接."问题解决方法! 时间:2018-12-20 本文章向大家介绍JDB ...

  9. java 发送邮件(SSL加密方式,含附件)

    java 发送邮件(SSL加密方式) 一.基于javamail发送邮件 二.基于Apache提供的commons-email发送邮件 一.基于javamail发送邮件 maven: <depen ...

最新文章

  1. PhpCms V9调用指定栏目子栏目文章的方法
  2. 我的Windows Vista™也装好了...
  3. 详细说明通过kettke对csv文件转换的操作步骤_如何将多页面pdf分割成一页一页的PDF文件...
  4. 三足鼎立 —— GPM 到底是什么?(一)
  5. OpenCV: 图像连通域检测的递归算法
  6. 在Cloud9上搭建Yii开发环境
  7. 7-44 基于词频的文件相似度 (30 分)(思路加详解+set容器简便做法)兄弟们冲呀呀呀呀呀 今天你AC了吗
  8. 如何在 PyFlink 1.10 中自定义 Python UDF?
  9. SpringBoot时间格式化的5种方法!
  10. angularjs--控制器的显示与隐示使用
  11. Linux下将pycharm图标添加至桌面
  12. 面向对象程序设计概述(金老师第一讲)
  13. Windows 底层驱动级 Anti-Rootkit 工具 ScDetective 源代码
  14. ubuntu下输入法突然崩溃(只能选择第一个预选词,选择其他预选词会变成数字)的解决办法
  15. get_digits
  16. iOS创建苹果证书、制作p12证书流程
  17. Fcitx 在 LumaQQ中的设置
  18. HTML+CSS实现小米官网首页
  19. GPS定位原理、系统组成及工作频段
  20. 使用Photoshop的总结

热门文章

  1. Could not resolve placeholder ‘‘“ in value ““
  2. java计算机毕业设计网上鲜花店系统源程序+mysql+系统+lw文档+远程调试
  3. 使用Phantomjs和ChromeDriver添加Cookies的方法
  4. python高级编程 python 笔记1
  5. 远航汽车:坚持合作共赢经营理念 携手志同道合者共创美好未来
  6. sd卡和tf卡的一些常识
  7. 使用java直接生成pdf保存至本地路径
  8. 英语翻译——中译英1
  9. 盘后股价应声下跌1.54% 电信业“亚马逊“Twilio路在何方?
  10. 【前端基础】简单介绍什么是软件