I’m using the latest .NET 8’s HttpClient
to call an API endpoint.
This is my code:
var cert = new X509Certificate("myCert.pfx", "mypass");
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual
};
handler.ClientCertificates.Add(cert);
using var httpClient = new HttpClient(handler);
var response = await httpClient.PostAsJsonAsync("https://<my-url>");
var resultString = await response.Content.ReadAsStringAsync();
It generated the following exception:
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.IO.IOException: The decryption operation failed, see inner exception.
---> System.ComponentModel.Win32Exception (0x80090326): The message received was unexpected or badly formatted.
--- End of inner exception stack trace ---
at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
at System.Net.Http.HttpConnection.InitialFillAsync(Boolean async)
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
This seems to suggest that the private key is not working as expected. I used the same certificate file and password on Postman and the request succeeded and returned meaningful results.
Here are some of the things I have tried:
- Double-checked my password
- Breaking up the pfx file into a certificate and private key
- Specify the request as TLS 1.2
None of the above worked.
3
Your code is mixing types meant for .NET Framework (4.x) and older versions of .NET Core; ostensibly code using them should work without modification on .NET 8 but the program won’t run as well as it could – though in practice I find that’s going to cause errors like “The message received was unexpected or badly formatted.”.
-
X509Certificate2
should be used instead ofX509Certificate
(which dates back to .NET Framework 1.x in 2002; whereasX509Certificate2
supplanted it and was added in .NET Framework 2.0 in 2005). -
HttpClientHandler
is only for .NET Framework and .NET Core 1 + 2, and not .NET 8 – you should be usingSocketsHttpHandler
. (yes, this is confusing and inconsistently documented).- Though
HttpClientHandler
is still available in .NET 8, internally it’s just a wrapper overSocketsHttpHandler
.
- Though
-
I note that your reported error
0x80090326
is indicative of an inability of both-ends to agree on cipher-suite algo).
I suggest that you change your code to this and give it a spin:
X509Certificate2 clientCert = new X509Certificate2(
fileName: "myCert.pfx",
password: "mypass"
);
if( !clientCert.HasPrivateKey ) throw new InvalidOperationException( "Private key not loaded." );
SocketsHttpHandler socksHandler = new SocketsHttpHandler
{
SslOptions =
{
// CipherSuitesPolicy = new CipherSuitesPolicy( ... ) // Only uncomment this if the underlying TLS error is due to a client/server disagreement over what TLS algos to use and this is the *only* way to resolve it.
ClientCertificates = new X509CertificateCollection()
{
clientCert
},
// SslProtocols = SslProtocols.Tls12 // Only uncomment this if you actually really need to specify exactly TLS 1.2. By default it will be negotiated.
}
};
using HttpClient httpClient = new HttpClient( socksHandler );
using HttpResponseMessage response = await httpClient.PostAsJsonAsync( "https://<my-url>", etc );
String responseBody = await response.Content.ReadAsStringAsync();