6

Scenario: I have a client implemented in C# that shoud connect to a server using gRPC using SSL for an encrypted Connection. However, the certificate used by the server may or may not be self-signed.

In the docs, I have only seen that I can set up a channel credential either insecure (no SSL at all) or secure by using custom root certificates (or using the public root CAs which will not validate a self-signed cert), which effectively means I would have to make sure that I install the self-signed server certificate as root. Basically, how do I do that programmatically?

var channelCredentials = new SslCredentials(rootAsPem); 
// FIXME: specify that channelCredentials can accept self-signed certificates or fetch certificates?
var channel = new Channel("myservice.example.com", channelCredentials);
var client = new Greeter.GreeterClient(channel);

What I would like to implement is to ask the user like "hey, the server that you configured uses a self-signed certificate, are you OK with that?" and if so, install the certificate as a root certificate in the PEM.

My main Questions now are:

  1. How do I even get the server certificate? All I currently get is an exception.
  2. Is it possible to avoid having to install the server certificate as a root certificate?
Georg
  • 5,626
  • 1
  • 23
  • 44

3 Answers3

9
var httpClientHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpClientHandler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greet.GreeterClient(channel);

https://learn.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0

Gasha
  • 91
  • 1
  • 2
  • Thanks for the hint, but unfortunately, I also have to support .NET Framework. – Georg Oct 25 '19 at 12:35
  • Your example implies that you'd need to create a new channel for every client. You don't want to do that. Here's the (better) example from the link you provided: ``var httpHandler = new HttpClientHandler(); httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = httpHandler }); var client = new Greet.GreeterClient(channel); `` – Elroy Flynn Apr 25 '23 at 14:45
  • sorry about the lack of code formatting, why doesn't backtick work? (I tried one and three)) – Elroy Flynn Apr 25 '23 at 14:51
4

I had a similar problem and finally found a solution to establish HTTPs connection between

  • .NET Framework 4.7.2 client (WPF app) and
  • ASP .NET Core 3.1 gRPC Server (Console).

They key to a solution was to first download the server certificate using a regular HttpClient and a Get on the the gRPC target server. Through the HttpClientHandler with its ServerCertifacteCustomValidationCallback, you are able to get the X509 Certificate of the server and check if it is a self-signed certificate to prompt the user for confirmation of a probably unsafe connection. If it is confirmend, you can export the certificate to a PEM formatted string and than use it in the constructor of SslCredentials. There is another important thing: the certificate has to contain the DNS name or the IP of the URL through which you are contacting the server, because the client performs a check on this. You can see a detailed error by enabling gRPC client debugging:

See my more detailed answer here:

https://stackoverflow.com/a/63565090/378415

JanW
  • 1,799
  • 13
  • 23
1

For those who have the same problem: It is currently not possible, unfortunately. The C library does allow this, but the C# wrapper does not. There is a pull request on its way, but not yet merged: https://github.com/grpc/grpc/pull/17051

Georg
  • 5,626
  • 1
  • 23
  • 44
  • 1
    Actually, the callback is merged in the meantime, but only allows to discard Connections that are already established. More Options are to come, but only when an entirely new API is matured and then exposed in the language wrappers. – Georg Mar 19 '20 at 09:25