question

TaB-8489 avatar image
0 Votes"
TaB-8489 asked TaB-8489 commented

Dynamic certificate update is failing in HTTPClient Handler for mutual authentication?

Team,

I have made an api in .net core 3.1 and created a custom HTTP Client Handler to manage dynamic certificates per http call but, the calls with only first certificate remains successful. Any call for other certificates gives authentication error from server side because ClientCertificates.Clear() is not working.

Startup.cs

 services.AddHttpClient(Configuration["ClientName"], c =>
              {
                  c.BaseAddress = new Uri(Configuration["URL"]);
              }).ConfigurePrimaryHttpMessageHandler<RegistryHttpHandler>()
    
    
  service.AddSingleton<IHttpClient, HttpClient>(); //My custom http client class 
    
  service.AddTransient<RegistryHttpHandler>();  // Here life time doesn't matter because Http Client is singleton

The code for send async is:

  using (var client = _clientFactory.CreateClient(clientName))
                 {
                     using (var response = await client.SendAsync(request))
                     {
                         if (response != null)
                         {
                             data= await response.Content.ReadAsStringAsync();
                         }                        
                     }
                 }

The code for certificate handler is:

 public class CertificateRegistryHttpHandler : HttpClientHandler, IDisposable
     {
         private readonly IConfiguration _configuration;
         private readonly Dictionary<string, X509Certificate2> _certMap;
         private bool disposedValue;
         public CertificateRegistryHttpHandler(IConfiguration configuration) : base()
         {
             _configuration = configuration;
             _certMap = new Dictionary<string, X509Certificate2>() { { configuration["DefaultCertificateName"], new X509Certificate2(fileName: Path.Combine(configuration["CertificatePath"], configuration["DefaultCertificateName"]), password: _configuration[configuration["DefaultCertificateName"]]) } };
         }
    
         protected override async Task<HttpResponseMessage> SendAsync(
                 HttpRequestMessage request,
                 CancellationToken cancellationToken)
         {
             var appId = (string)Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(request.RequestUri.Query)["myID"];
             string certName = _configuration[appId];
             if (!string.IsNullOrEmpty(certName))
             {
                 string certPass = _configuration[certName];
                 if (!string.IsNullOrEmpty(certPass))
                 {
                     if (!_certMap.ContainsKey(certName))
                     {
                         string certPath = Path.Combine(_configuration["CertificatePath"], certName);
                         if (File.Exists(certPath))
                         {
                             _certMap.Add(certName, new X509Certificate2(fileName: certPath, password: certPass));
                             AddCertificate(certName);
                         }
                         else
                         {
                             AddCertificate(_configuration["DefaultCertificateName"]);
                         }
                     }
                     else
                     {
                         AddCertificate(certName);
                     }
    
                 }
                 else
                 {
                     AddCertificate(_configuration["DefaultCertificateName"]);
                 }
             }
             else
             {
                 AddCertificate(_configuration["DefaultCertificateName"]);
             }
             return await base.SendAsync(request, cancellationToken);
         }
         private void AddCertificate(string certName)
         {
             if (_certMap[certName]!=null && !ClientCertificates.Contains(_certMap[certName]))
             {
                 ClientCertificates.Clear();
                 ClientCertificates.Add(_certMap[certName]);
             }
         }
         protected override void Dispose(bool disposing)
         {
             if (!disposedValue)
             {
                 if (disposing)
                 {
                     _certMap.Values.All(x =>
                     {
                         x.Dispose();
                         return true;
                     });
                     _certMap.Clear();
                 }
                 base.Dispose();
                 disposedValue = true;
             }
         }
         public new void Dispose()
         {
             Dispose(disposing: true);
             GC.SuppressFinalize(this);
         }
     }
 }


In debug mode, I have cleared the pervious certificate and added the new one. Still, httpclient is sending the request with the old one and server returns authentication error.

But, on waiting for around 3 minutes in the debug the request with updated certificate also works. Then again update the certificate and wait for around 3 minutes. Please suggest how to updated the certificates in the ClientCertificates collection of httpclienthandler immediately as I'm facing error in production environment.

dotnet-runtimedotnet-aspnet-core-webapi
· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi @TaB-8489,
What server are you using? In addition, does your requirement require a different certificate for each request?

0 Votes 0 ·

@ChaoDeng-MSFT , Hello. Thank you for helping.

This is happening in my local machine as well. I have windows 10 Enterprise version 1909 ( OS build 1863.1621)

I have a use case where I have to change the certificate dynamically for different requests.

Consider, if I make a request which needs a new certificate then after updating the client certificate collection I have to wait for at least 3 minutes. Once wait completes then I can get the data with the help of updated certificate. Otherwise, if I do not wait for 3 minutes and make the countless requests then I keep on getting the authentication error till eternity. I have to give around 2 to 3 minutes of idle state.


0 Votes 0 ·

@ChaoDeng-MSFT , Hope you're doing well. Once you get any info on this please let me know. I'm super stuck with this in turn impacting my production environment

0 Votes 0 ·

Can anyone help on this issue.

0 Votes 0 ·

0 Answers