How to enable server side SSL for gRPC?
Asked Answered
F

3

19

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) }
};
Fremantle answered 8/6, 2016 at 23:26 Comment(0)
F
66

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.

Fremantle answered 10/6, 2016 at 3:3 Comment(5)
IMPORTANT: replace %COMPUTERNAME% with domain name of server machine If it has IP address only, and you cannot grant it domain name, try to 1) %COMPUTERNAME% = my-server 2) edit /etc/hosts at client machine and add line 111.111.111.111 my-server where 111.111.111.111 is ip address of the server machineScriabin
@NikolayPakudin After doing the above implementation getting below error in client, StatusCode=Unavailable, Detail="DNS resolution failed". Any idea how to resolve this issue?Epistasis
@ÁlvaroGarcía You need to be able to make a DNS lookup on the server name from the client. In terms of Windows machines, you may execute PING COMPUTERNAME, but it may not resolve using DNS. As noted in Nikolays comment above, you then need to make sure nslookup SERVERNAME works on your client computers.Chickenhearted
As a simple workaround, you can specify the ip address for the server, and add channel option to override the name of the server, so for instance: var options = new List<ChannelOption> { new ChannelOption(ChannelOptions.SslTargetNameOverride, "ACTUAL-HOSTNAME") }; var channel = new Channel("1.2.3.4", 555, ssl, options);Chickenhearted
Your batch file mentions openssl.cfg. There is no such file in my system. What should be in it? (sample contents).Af
S
5

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

Scriabin answered 25/7, 2019 at 23:25 Comment(0)
A
2

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.

Ashmore answered 23/5, 2020 at 7:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.