Skip to content

HttpClient SSL Connection with Client Certificate Fails After Code Change #86199

Open
@sflanker

Description

@sflanker

Description

I have a C# console app that makes an HttpClient connection that uses a Client Certificate for authentication. It works fine when I initially run it. However after making a completely unrelated/innocuous code change, the next time I run it I get the following error:

Unhandled exception. 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.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AppleCrypto+SslException: The user name or passphrase you entered is not correct.
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Program.<Main>$(String[] args) in Program.cs:line 19

If I run dotnet clean before running again, it resolves the issue.

Reproduction Steps

Here is my test application:

using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

var handler = new HttpClientHandler();

var url = args[0];
var clientCert = File.ReadAllText(args[1]);
var clientKey = File.ReadAllText(args[2]);

handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(
  X509Certificate2.CreateFromPem(clientCert, clientKey)
);

var httpClient = new HttpClient(handler);
var responseTask = httpClient.GetStringAsync(url);
responseTask.Wait();
var response = responseTask.Result;
Console.WriteLine(response);

I run this program with something like the following:

dotnet run -- "https://127.0.0.1:57720/api/v1/namespaces" client_cert.crt client_cert.key

I'm connecting to a run-of-the-mill k3d cluster using Docker Desktop for Mac on Mac OS Monterey. I generate the crt and key with the following commands:

yq '.users[0].user.client-certificate-data' ~/.kube/config | base64 -d > client_cert.crt
yq '.users[0].user.client-key-data' ~/.kube/config | base64 -d > client_cert.key

Expected behavior

Running the program after changing the code should successfully connect and authenticate with the client certificate, even after changing the code, without running dotnet clean.

Actual behavior

Unhandled exception. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AppleCrypto+SslException: The user name or passphrase you entered is not correct.

Regression?

No response

Known Workarounds

running dotnet clean after every code change.

Configuration

MacOS 12.6.3
.Net 7.0.202
Docker Desktop 4.16.2
k3d version v5.4.1
k3s version v1.22.7-k3s1

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions