Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enable server side SSL for gRPC?

New to gRPC and couldn't really find any example on how to enable SSL on the server side. I generated a key pair using openssl but it complains that the private key is invalid.

D0608 16:18:31.390303 Grpc.Core.Internal.UnmanagedLibrary Attempting to load native library "...\grpc_csharp_ext.dll"
D0608 16:18:31.424331 Grpc.Core.Internal.NativeExtension gRPC native library loaded successfully.
E0608 16:18:43.307324 0 ..\src\core\lib\tsi\ssl_transport_security.c:644: Invalid private key.
E0608 16:18:43.307824 0 ..\src\core\lib\security\security_connector.c:821: Handshaker factory creation failed with TSI_INVALID_ARGUMENT.
E0608 16:18:43.307824 0 ..\src\core\ext\transport\chttp2\server\secure\server_secure_chttp2.c:188: Unable to create secure server with credentials of type Ssl.

Here's my code

var keypair = new KeyCertificatePair(
            File.ReadAllText(@"root-ca.pem"),
            File.ReadAllText(@"ssl-private.key"));
SslServerCredentials creds = new SslServerCredentials(new List<KeyCertificatePair>() {keypair});
Server server = new Server
{
    Services = { GrpcTest.BindService(new GrpcTestImpl()) },
    Ports = { new ServerPort("127.0.0.1", Port, creds) }
};
like image 241
qmo Avatar asked Jun 08 '16 23:06

qmo


People also ask

Does gRPC need SSL?

In general this mechanism must be used as well as SSL/TLS on the channel - Google will not allow connections without SSL/TLS, and most gRPC language implementations will not let you send credentials on an unencrypted channel.

Can gRPC use https?

And TLS implies https. So there's no way "to use insecure grpc with https", as at that point it is then http. There is no double-encryption. The gRPC security mode is the same as the HTTP security mode.

Is gRPC encrypted by default?

When you're using gRPC over a TLS-encrypted HTTP/2 connection, all traffic between clients and servers is encrypted, even if you don't use channel-level authentication. This chapter will show how to apply call credentials and channel credentials to a gRPC service.


3 Answers

Here's what I did.

Using OpenSSL, generate certificates with the following:

@echo off
set OPENSSL_CONF=c:\OpenSSL-Win64\bin\openssl.cfg   

echo Generate CA key:
openssl genrsa -passout pass:1111 -des3 -out ca.key 4096

echo Generate CA certificate:
openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=MyRootCA"

echo Generate server key:
openssl genrsa -passout pass:1111 -des3 -out server.key 4096

echo Generate server signing request:
openssl req -passin pass:1111 -new -key server.key -out server.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=%COMPUTERNAME%"

echo Self-sign server certificate:
openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

echo Remove passphrase from server key:
openssl rsa -passin pass:1111 -in server.key -out server.key

echo Generate client key
openssl genrsa -passout pass:1111 -des3 -out client.key 4096

echo Generate client signing request:
openssl req -passin pass:1111 -new -key client.key -out client.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=%CLIENT-COMPUTERNAME%"

echo Self-sign client certificate:
openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

echo Remove passphrase from client key:
openssl rsa -passin pass:1111 -in client.key -out client.key

Change password 1111 to anything you like

Server:

var cacert = File.ReadAllText(@"ca.crt");
var servercert = File.ReadAllText(@"server.crt");
var serverkey = File.ReadAllText(@"server.key");
var keypair = new KeyCertificatePair(servercert, serverkey);
var sslCredentials = new SslServerCredentials(new List<KeyCertificatePair>() { keypair }, cacert, false);

var server = new Server
{
    Services = { GrpcTest.BindService(new GrpcTestImpl(writeToDisk)) },
    Ports = { new ServerPort("0.0.0.0", 555, sslCredentials) }
};
server.Start();

Client:

var cacert = File.ReadAllText(@"ca.crt");
var clientcert = File.ReadAllText(@"client.crt");
var clientkey = File.ReadAllText(@"client.key");
var ssl = new SslCredentials(cacert, new KeyCertificatePair(clientcert, clientkey));
channel = new Channel("localhost", 555, ssl);
client = new GrpcTest.GrpcTestClient(channel);

If "localhost" doesn't work, use the host name instead.

like image 84
qmo Avatar answered Oct 16 '22 09:10

qmo


If usage of Certificate Authority (CA) and Certificate Signing Request (CSR) is too sophisticated for your task, you can use self-signed certificates.

Let say, there is 1 server and 2 (or more) clients.

Execute at client1:

openssl req -x509 -newkey rsa:4096 -nodes -keyout client.key -out client.crt -days 3650 -subj '/CN=client1' # generate client1 cert and key
sudo bash -c 'echo "192.168.1.101 my.server" >> /etc/hosts' # create domain for server - if necessary only
scp client.crt [email protected]:/path/to/certs/client1.crt # copy public cert client1 to server machine

Execute at client2:

openssl req -x509 -newkey rsa:4096 -nodes -keyout client.key -out client.crt -days 3650 -subj '/CN=client2' # generate client2 cert and key
sudo bash -c 'echo "192.168.1.101 my.server" >> /etc/hosts' # create domain for server- if necessary only
scp client.crt [email protected]:/path/to/certs/client2.crt # copy public cert client2 to server machine

Execute at server:

openssl req -x509 -newkey rsa:4096 -nodes -keyout server.key -out server.crt -days 3650 -subj '/CN=my.server' # generate server cert and key
scp server.crt client1-user@client1-addr:/path/to/certs # copy public cert server to client1 machine
scp server.crt client2-user@client2-addr:/path/to/certs # copy public cert server to client2 machine
cat client1.crt client2.crt > client.crt # combine client certs into the single file

Server code:

var clientCert = File.ReadAllText(Path.Combine(certPath, "client.crt"));
var serverCert = File.ReadAllText(Path.Combine(certPath, "server.crt"));
var serverKey = File.ReadAllText(Path.Combine(certPath, "server.key"));
var keyPair = new KeyCertificatePair(serverCert, serverKey);
var credentials = new SslServerCredentials(new List<KeyCertificatePair> { keyPair }, clientCert, true);

var server = new Server
{
    Services = { MyService.BindService(new MyAdminService()) },
    Ports = { new ServerPort("0.0.0.0", 54321, credentials) }
};

Client code:

var serverCert = File.ReadAllText(Path.Combine(_certPath, "server.crt"));
var clientCert = File.ReadAllText(Path.Combine(_certPath, "client.crt"));
var clientKey = File.ReadAllText(Path.Combine(_certPath, "client.key"));
var credentials = new SslCredentials(serverCert, new KeyCertificatePair(clientCert, clientKey));

var channel = new Channel("my.server:54321", credentials);    
var client = new MyService.MyServiceClient(channel);

IMPORTANT!

To use TLS certificates, use a domain name when generate server certificate.

Client certificates can use any unique string.

Domain name should contain at least 1 dot (.), e.g. my.server or my.server.customzone

If use top-level domain like my-server, it causes a long waiting to resolve it (for me it always about 76 seconds).

Pros: - no need to generate CSR, pass it to machine with CA, sign it there and copy back to originating machine

Cons: - adding new client requires adding certificate to server

like image 30
Nikolay Pakudin Avatar answered Oct 16 '22 07:10

Nikolay Pakudin


If you have tried what @qmo has suggested and still not working and you're getting the same error saying "StatusCode=Unavailable, Detail="DNS resolution failed" I fixed it by adding a new record in my host file (located in C:\Windows\System32\drivers\etc for Windows).

127.0.0.1 DESKTOP-QNCI7UN

Where DESKTOP-QNCI7UN is the name of my machine. Then in the client I'm using:

channel = new Channel("DESKTOP-QNCI7UN", 50000, ssl);

By using "locahost" it was not working. So by using the machine name in the client + added record in the host file fixed the issue.

like image 35
Martin Dimitrov Avatar answered Oct 16 '22 08:10

Martin Dimitrov