question

TaB-8489 avatar image
0 Votes"
TaB-8489 asked KarthikKarunanithy-7287 published

HTTPClientHandler throwing exception when handling certificates dynamically within send async.

Team,

I have build an API in .net core 3.1 with custom certificate handler for dynamically updating the certificate in sendasync request for mutual TLS authentication. The api app service deployment type is framework dependent.

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);
         }
     }
 }

Every time I'm sending the request it returns the correct output but In addition, I'm receiving this exception i.e.

System.AggregateException: One or more errors occurred. (The SSL connection could not be established, see inner exception.) ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.ArgumentNullException: Value cannot be null. (Parameter 'value') at System.Collections.CollectionBase.OnValidate(Object value) at System.Security.Cryptography.X509Certificates.X509CertificateCollection.OnValidate(Object value) at System.Collections.CollectionBase.System.Collections.IList.Add(Object value) at System.Security.Cryptography.X509Certificates.X509CertificateCollection.AddRange(X509CertificateCollection value) at System.Net.Security.CertificateHelper.GetEligibleClientCertificate(X509CertificateCollection candidateCerts) at System.Net.Http.HttpClientHandler.b_70_0(Object sender, String targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, String[] acceptableIssuers) at System.Net.Security.SslStream.UserCertSelectionCallbackWrapper(String targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, String[] acceptableIssuers) at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint) at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output) at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count) at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.ProcessAuthentication(LazyAsyncResult lazyResult, CancellationToken cancellationToken) at System.Net.Security.SslStream.BeginAuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken, AsyncCallback asyncCallback, Object asyncState) at System.Net.Security.SslStream.<>c.b65_0(SslClientAuthenticationOptions arg1, CancellationToken arg2, AsyncCallback callback, Object state) at System.Threading.Tasks.TaskFactory`1.FromAsyncImpl[TArg1,TArg2](Func`5 beginMethod, Func`2 endFunction, Action`1 endAction, TArg1 arg1, TArg2 arg2, Object state, TaskCreationOptions creationOptions) at System.Threading.Tasks.TaskFactory.FromAsync[TArg1,TArg2](Func`5 beginMethod, Action`1 endMethod, TArg1 arg1, TArg2 arg2, Object state, TaskCreationOptions creationOptions) at System.Threading.Tasks.TaskFactory.FromAsync[TArg1,TArg2](Func`5 beginMethod, Action`1 endMethod, TArg1 arg1, TArg2 arg2, Object state) at System.Net.Security.SslStream.AuthenticateAsClientAsync(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken) at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Middlewares.CertificateRegistryHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Polly.CircuitBreaker.AsyncCircuitBreakerEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext, ExceptionPredicates shouldHandleExceptionPredicates, ResultPredicates`1 shouldHandleResultPredicates, ICircuitController`1 breakerController) at Polly.AsyncPolicy`1.ExecuteAsync(Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at Polly.Wrap.AsyncPolicyWrapEngine.<>cDisplayClass0_0`1.<b0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Polly.Fallback.AsyncFallbackEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldHandleExceptionPredicates, ResultPredicates`1 shouldHandleResultPredicates, Func`3 onFallbackAsync, Func`4 fallbackAction, Boolean continueOnCapturedContext) at Polly.AsyncPolicy`1.ExecuteAsync(Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at Polly.Wrap.AsyncPolicyWrapEngine.ImplementationAsync[TResult](Func`3 func, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext, IAsyncPolicy`1 outerPolicy, IAsyncPolicy`1 innerPolicy) at Polly.AsyncPolicy`1.ExecuteAsync(Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at Microsoft.Extensions.Http.PolicyHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Services.HttpClient.Get(String url, String clientName) --- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) at Services.SerlializeService.GetData[T](String queryStringParam, String clientName) at Secure.Cyber.CyberResponse.RequestCyber_Credentials(String objectName, String safe, String user) at AutomationGateway.Controllers.CyberCredentialsController.Credentials(String safe, String objectname) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.gAwaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.gAwaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.gAwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g_Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)


Generally SSL exception comes with System.Security.Authentication.AuthenticationException. In that case we can understand that https negotiation is not going through properly. But, I'm unable to figure out why it is coming with System.ArgumentNullException due to which unable to find out any resolution. I would be grateful if some can help on this. Thank you.

Regards,



dotnet-runtimedotnet-aspnet-core-webapiazure-webapps-ssl-certificatesazure-webapps-apis
· 9
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.

We have reached out to this customer offline to offer further support. If the community has any suggestions, we encourage you to continue the discussion.

0 Votes 0 ·

@brtrach-MSFT , Thanks a lot for helping. But the problem is that we have hosted this service in Azure Stack i.e. On-prem cloud cluster of Azure. Should I share the subscription id of that one.

Tried all hit and trials but unable to resolve this error. I don't know how SSL error is combined with Value null exception

1 Vote 1 ·

@TaB-8489, yes please still follow the steps provided in the private message. I believe we should have an Azure Stack support team who might be able to assist.

1 Vote 1 ·
Show more comments

Hi @brtrach-MSFT - Is there any resolution for this, I'm facing a similar issue and posted the details below

0 Votes 0 ·

1 Answer

KarthikKarunanithy-7287 avatar image
0 Votes"
KarthikKarunanithy-7287 answered KarthikKarunanithy-7287 published

Hi @brtrach-MSFT - Was there any solution for this issue? I'm facing a similar issue when trying to make calls with httpclient handler and I'm retrieving the certificates from keyvault. I didn't get the issue consistently as it happens only when the application runs for several days , after restarting the service things work as normal


     services.AddHttpClient("CertConfig").
               ConfigurePrimaryHttpMessageHandler(() =>
               {
                   var handler = new HttpClientHandler();
                   X509Certificate2 signingCertificate = new  GetCertificateFromKeyvault(Configuration);
                   handler.ClientCertificates.Add(signingCertificate);
                   return handler;
               });


System.ArgumentNullException:
at System.Collections.CollectionBase.OnValidate (System.Collections.NonGeneric, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)
at System.Security.Cryptography.X509Certificates.X509CertificateCollection.OnValidate (System.Security.Cryptography.X509Certificates, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)
at System.Collections.CollectionBase.System.Collections.IList.Add (System.Collections.NonGeneric, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)
at TestCertService.Startup.<ConfigureServices>b_8_0 (TestCertService, Version=3.1.16.0, Culture=neutral, PublicKeyToken=null: D:\a\1\s\TestCertService\Startup.cs:70)
at Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions+<>c
DisplayClass5_0.<ConfigurePrimaryHttpMessageHandler>b1 (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.Http.DefaultHttpClientFactory+<>c
DisplayClass17_0.<CreateHandlerEntry>gConfigure|0 (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.Http.LoggingHttpMessageHandlerBuilderFilter+<>c
DisplayClass2_0.<Configure>b0 (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateHandlerEntry (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at System.Lazy`1.ViaFactory (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Lazy`1.CreateValue (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateHandler (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateClient (Microsoft.Extensions.Http, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at TestCertService.ctor (TestCertService, Version=3.1.16.0, Culture=neutral, PublicKeyToken=null: D:\a\1\s\TestCertService\CertConnector.cs:49)
at ResolveService (Microsoft.Extensions.DependencyInjection, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService (Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.14.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at lambda_method (Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider+<>c
DisplayClass5_0.<CreateControllerFactory>gCreateController|0 (Microsoft.AspNetCore.Mvc.Core, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next (Microsoft.AspNetCore.Mvc.Core, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync (Microsoft.AspNetCore.Mvc.Core, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker+<<InvokeFilterPipelineAsync>g
Awaited|19_0>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker+<<InvokeAsync>g
Logged|17_1>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Microsoft.AspNetCore.Routing.EndpointMiddleware+<<Invoke>g
AwaitRequestTask|6_0>d.MoveNext (Microsoft.AspNetCore.Routing, Version=3.1.10.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware+<Invoke>d
5.MoveNext (Swashbuckle.AspNetCore.SwaggerUI, Version=5.6.3.0, Culture=neutral, PublicKeyToken=4232c99127b3c254)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware+<Invoke>d
_4.MoveNext (Swashbuckle.AspNetCore.Swagger, Version=5.6.3.0, Culture=neutral, PublicKeyToken=62657d7474907593)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)





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.