2

I am attempting to create a gRPC server and client using ssl (with .NET 5 and VS2019). I want to use a generated X509Certificate2 as a root certificate to generate other client certificates. For that, I wrote a helper class CertificateUtil, following these threads: How can I create a self-signed certificate using C#? Generate and Sign Certificate Request using pure .net Framework.

Next, the root certificate should be registered as a custom trust store in the startup settings of the gRPC server, and the client should connect using the generated client certificate.

I have the following question:

Is it possible to register a custom trust store in gRPC?

  • If not, what is a good alternative?
  • If yes, what part of the process I explain below is incorrect?

Currently, I am getting the following errors: client: "Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. IOException: Received an unexpected EOF or 0 bytes from the transport stream." server: "The local security authority (LSA) is unreachable"

Steps to reproduce:

  1. Pull the following MWE: https://github.com/Renopph/GrpcServerClient
  2. Uncomment lines 10 and 11 in GprcCert/Program.cs and run. This should create two certificate files, GrpcServer.pfx and GrpcClient.pfx. Set both files' properties to Copy always. Do NOT register these certificates in your system's trust store.
  3. Place GrpcClient.pfx in the root of the GrpcClient project.
  4. Comment out lines 10 and 11, and uncomment line 12 in GprcCert/Program.cs.
  5. Right click the Solution, open Properties. Select "Multiple startup projects" and set both GrpcCertand GrpcClient to "Start". Then run the solution (should run GrpcCert first, then GrpcClient).
  6. The client and server both show the aforementioned errors.

I also tried leaving out the KestrelServerOptions in the Startup.cs of the server. This allowed any client to connect, even without the certificate.

Renopp
  • 21
  • 4
  • Regarding your question *Is it possible to register a custom trust store in gRPC?* I think the answer is definitely *yes*. gRPC (or more specifically the Kestrel which hosts the gRPC services) does not care from where the certificates come, as long as the underlying certificate structure works. I used a *certificate service* implementation that fetches certificates from the local machine store for a gRPC service in .NET 6, and I also used locally generated self-signed certificates, and both worked. The Kestrel didn't know if it came from a custom trusted store, file path or local machine store – dan-kli Feb 05 '23 at 19:51
  • So I do not think it matters where the certificates come from, as long as the underlying certificate structure works. But I think here lies the problem, it took me some time to get both certificate structures (the certificates signed by my company that are located in the *Trusted Root Certification Authorities certificate store* and the self-signed certs) to work, and the error messages were kinda cryptic. – dan-kli Feb 05 '23 at 19:58
  • If you want to I can write up the certificate service that I implemented and used to inject the certificates (of type x509certificate2) on the client and server as an answer, but I cannot tell you what exactly is wrong with your certificates. It took me quite some fixing to get my certificates to work with gRPC, and yours look completely different.. so it would only be half an answer really to your question. – dan-kli Feb 05 '23 at 20:00
  • If you can provide the answer, that would be very nice. Even if it does not give the full answer, I will have additional input as to what is wrong with my approach. – Renopp Feb 06 '23 at 09:18
  • I will write it up tomorrow after work, I don't have time today. Sadly I think it will not help you that much, but we will see – dan-kli Feb 08 '23 at 07:53

1 Answers1

1

I will write up this answer, but as I already said I think it only answers half your questions. Regarding your question Is it possible to register a custom trust store in gRPC? I think the answer is yes, as long as you fulfill the TLS requirements of gRPC and the underlying certificate structure works, it should be possible. According to the MS Documentation for certificate authentication, the certificate authentication happens at the TLS level, long before it ever gets to ASP.NET Core, therefore the Kestrel (which hosts the gRPC services) does not care if the (root) certificate comes from a custom trust store or the local machine store (or somewhere else).

It took me a while to get both my self-signed certs in the local machine store and my company's certs in the Trusted Root Certification Authorities Certificate Store to work with gRPC, therefore it's maybe easier to get your certificates first to a point where they just work with the root cert getting fetched from the local machine store, and then move it to a custom store.


This is the service I used to inject the certs on the client- and serverside (in .NET 6), I think you could extend it easily to fetch a cert from any other location (like a custom certificate store):

using System.Security.Cryptography.X509Certificates;

namespace Shared.Certificates
{
    public class CertificateService : ICertificateService
    {
        public X509Certificate2 GetCertificateFromLocalMachineStore(string friendlyName)
        {
            var store = GetLocalMachineCertificates();
            X509Certificate2 certificate = null;
            foreach (var cert in store.Cast<X509Certificate2>().Where(cert => cert.FriendlyName.Equals(friendlyName)))
            {
                certificate = cert;
            }
            return certificate;
        }

        private static X509Certificate2Collection GetLocalMachineCertificates()
        {
            var localMachineStore = new X509Store(StoreLocation.LocalMachine);
            localMachineStore.Open(OpenFlags.ReadOnly);
            var certificates = localMachineStore.Certificates;
            localMachineStore.Close();
            return certificates;
        }
    }
}

And the interface for the service, which you could extend for a method like public X509Certificate2 GetCertificateFromCustomTrustStore(string friendlyName), where you could fetch your cert from wherever you want to store them:

using System.Security.Cryptography.X509Certificates;

namespace Shared.Certificates
{
    public interface ICertificateService
    {
        X509Certificate2 GetCertificateFromLocalMachineStore(string friendlyName);
    }
}

Server-side certificate injection with the service from above:

CertificateService service = new CertificateService();
X509Certificate2 cert = service.GetCertificateFromLocalMachineStore("grpc_cert");

builder.WebHost.ConfigureKestrel(opt =>
{
    opt.ConfigureHttpsDefaults(h =>
    {
        h.ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode.AllowCertificate;
        h.CheckCertificateRevocation = false;
        h.ServerCertificate = cert;
    });
}

And client-side certificate injection with the service from above:

CertificateService service = new CertificateService();
X509Certificate2 cert = service.GetCertificateFromLocalMachineStore("grpc_cert");
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(cert);

Channel = GrpcChannel.ForAddress($"https://{address}:{port}", new GrpcChannelOptions
{
    HttpHandler = handler
});

If you want to, I can also provide you with the script that I used to generate my self-signed certificates, but I don't think they are of much use to you, since you have different certificates. Sadly I cannot help you more, I think your main problem is somewhere in your certificate structure, and I don't know too much about certs ...

dan-kli
  • 568
  • 2
  • 13
  • Hm, maybe I can use something like this and inject my custom certificate into the local store instead: https://stackoverflow.com/questions/566570/how-can-i-install-a-certificate-into-the-local-machine-store-programmatically-us – Renopp Feb 21 '23 at 10:03
  • You can, but then it is stored in the local machine store (not a custom store). If you use the code from the answer you linked and add the cert to the local machine store, I think you can use all my code from here and it should work out of the box. – dan-kli Feb 21 '23 at 13:52