gRPC: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot

ComptonAlvaro 166 Reputation points
2022-01-13T12:04:34.787+00:00

I am trying to follow this example to how to use JWT authentication.

But I get the error: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot.

This is my code:

The server:

using GestorOrdenadores.Service.Grpc.DotNet.Services;
using ProtoBuf.Grpc.Server;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;



var builder = WebApplication.CreateBuilder(args);



builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
    {
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireClaim(ClaimTypes.Name);
    });
});


builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters =
                        new TokenValidationParameters
                        {
                            ValidateAudience = false,
                            ValidateIssuer = false,
                            ValidateActor = false,
                            ValidateLifetime = true,
                            IssuerSigningKey = GestorOrdenadores.Service.Grpc.DotNet.Autenticacion.SecurityKey
                        };
                });



builder.Services.AddCodeFirstGrpc();

var app = builder.Build();




if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();





app.MapGrpcService<GreeterService>();




app.MapGet("/generateJwtToken", context =>
{
    GestorOrdenadores.Service.Grpc.DotNet.Autenticacion miAutenticador = new GestorOrdenadores.Service.Grpc.DotNet.Autenticacion();

    return context.Response.WriteAsync(miAutenticador.GenerateJwtToken(context.Request.Query["name"]));
});




app.Run();

The client:

public class ClienteGrpcDotNet
{
    #region constructores
    private ClienteGrpcDotNet() { }

    private ClienteGrpcDotNet (IGreeterService paramIServicio)
    {
        _client = paramIServicio;
    }


    public static async Task<ClienteGrpcDotNet> Create()
    {
        string miStrRootAddress = "https://localhost:5001";

        IGreeterService miCliente = await GetServiceWithJwtAuthentication(miStrRootAddress);

        return new ClienteGrpcDotNet(miCliente);
    }



    private static async Task<IGreeterService> GetServiceWithJwtAuthentication(string paramStrRootAddress)
    {
        GrpcChannel miCanal = await CreateAuthenticatedChannel(paramStrRootAddress);
        IGreeterService miServicio = miCanal.CreateGrpcService<IGreeterService>();

        return miServicio;
    }
    #endregion constructores



    private readonly IGreeterService _client;


    #region métodos del servicio
    public async Task<string?> SayHelloAsync()
    {
        HelloReply miRespuesta = await _client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });

        return miRespuesta.Message;
    }
    #endregion métodos del servicio


    #region autenticación
    private static async Task<string> Authenticate(string paramAddress)
    {

        using var httpClient = new HttpClient();
        using var request = new HttpRequestMessage
        {
            RequestUri = new Uri($"{paramAddress}/generateJwtToken?name={HttpUtility.UrlEncode(Environment.UserName)}"),
            Method = HttpMethod.Get,
            Version = new Version(2, 0)
        };
        using var tokenResponse = await httpClient.SendAsync(request);
        tokenResponse.EnsureSuccessStatusCode();

        var token = await tokenResponse.Content.ReadAsStringAsync();

        return token;
    }




    private static async Task<GrpcChannel> CreateAuthenticatedChannel(string paramAddress)
    {
        string miToken = await Authenticate(paramAddress);


        CallCredentials credentials = CallCredentials.FromInterceptor((context, metadata) =>
        {
            if (!string.IsNullOrEmpty(miToken))
            {
                metadata.Add("Authorization", $"Bearer {miToken}");
            }
            return Task.CompletedTask;
        });


        GrpcChannel channel = GrpcChannel.ForAddress(paramAddress, new GrpcChannelOptions
        {
            Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
        });



        return channel;
    }
    #endregion autenticación
}

Thanks so much.

Developer technologies | ASP.NET | ASP.NET Core
{count} votes

1 answer

Sort by: Most helpful
  1. Anonymous
    2022-01-19T06:21:47.13+00:00

    Hi @ComptonAlvaro ,

    The .NET gRPC client requires the service to have a trusted certificate.

    If you are testing your app locally and the ASP.NET Core HTTPS development certificate is not trusted. For instructions to fix this issue, you should trust the develop certificate as this article shows.

    If you are calling a gRPC service on another machine and are unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate. The following code uses HttpClientHandler.ServerCertificateCustomValidationCallback to allow calls without a trusted certificate:

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

    Notice: Untrusted certificates should only be used during app development. Production apps should always use valid certificates.

    1 person found this answer helpful.
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.