6

I'm using protobuf-net.Grpc on a .NET Core server and trying to make calls from a .NET Framework (4.7.2.) gRPC Client. A full example is here: https://github.com/angelagyang/GRPCProtobufExample

Here is a snippet of my client:

var channelCreds = new SslCredentials(GetRootCertificates());
var channel = new Channel("localhost", 5001, channelCreds);
var greeter = channel.CreateGrpcService<IGreeterService>();

With this configuration, I get the error StatusCode="Unknown", Detail="Stream removed"... when calling the server. I am able to connect to the server if I set ClientCertificateMode = ClientCertificateMode.NoCertificate on the server. However, I want the server to require a client certificate and validate the certificate via thumbprint.

For example, in .NET Core, I can use Grpc.Net.Client to configure my channel like so:

var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
var channel2 = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
      HttpHandler = handler
});

Is there any way to configure a client with certificate in .NET Framework like this? I'm pretty new to gRPC/.NET and would appreciate any suggestions!

Angela Yang
  • 348
  • 3
  • 9
  • 2
    I found an example that is similar to yours but with some differences, maybe it can help: https://grpc.io/docs/guides/auth/ (look for the text **`C#`**) – Peter B Jul 08 '20 at 20:28
  • 1
    Really interesting question. I'll try and do some digging tomorrow. – Marc Gravell Jul 08 '20 at 21:13
  • GRPC is HTTP/2. Peter's link is only the SSL/TLS security that is done at beginning of the connection using TCP and is not the entire solution. The link at top of OP posting will not work all the time. It will not work when client and server are on the same machine because both use localhost. The code will also not work if machine has the loopback IP 127.0.0.1 as the localhost. The recommendation for making a TCP connection is Server always use IPAddress.Any as endpoint. Client should either connect to the IP of server or use DNS.GetHost and then using index 1 of the address array. – jdweng Jul 08 '20 at 23:13

2 Answers2

4

Solved and updated the original example: https://github.com/angelagyang/GRPCProtobufExample

You can configure a client certificate by creating a KeyCertificatePair to pass into SslCredentials. You will need the PEM encoded certificate chain and PEM encoded private key.

var keyCertPair = new KeyCertificatePair(File.ReadAllText($"{rootDir}/cert.pem"), File.ReadAllText($"{rootDir}/cert.key")); 
var channelCreds = new SslCredentials(GetRootCertificates(), keyCertPair);

For testing purposes, I used the self-signed certificates here: https://github.com/grpc/grpc/tree/master/src/core/tsi/test_creds

When debugging, set GRPC_VERBOSITY = DEBUG and GRPC_DEBUG = ALL. This can help clear up vague error messages. For example, I realized that the server certificate I was using to configure HTTPS did not include localhost.

Angela Yang
  • 348
  • 3
  • 9
  • I have a cert with a private key on my gRPC service, but do I need to use pfx on my client, why not .cer without a private key converted into PEM? Must I use a keyCertPair? How to set those variables in Visual Studio and C#? – Hrvoje Batrnek Nov 28 '20 at 23:46
0

From what I tried and made working, You can skip the effort of having to read both cert.pem and cert.key. Also the GetRootCertificate() would work only if server systems are hosted in a well knows trusted website like google.com or msdn.com or the likes of it and localhost. More detailed answer can be found here and here

If we intend to use our own host DNS with SSL, Generating a Server certificate in pfx assigned to your DNS and converting it to pem for the client app is your way to go. Using tools like openssl will help convert the certificate encoding.

openssl pkcs12 -in "<DiskLocationOfPfx>\ProjectName.pfx" -out "<TargetLocation>\certifcate.pem" -clcerts

Once you convert your server certificate to pem, You can use

var channelCreds = new SslCredentials(File.ReadAllText($"{rootDir}/cert.pem"));
var channel = new Channel("www.youdns.com", 5001, secureCredentials);
Jins Peter
  • 2,368
  • 1
  • 18
  • 37