I am attempting to send a self-signed client certificate using HttpClient
with the following code:
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(GetClientCertificate()); //Loads cert from a .pfx file
var client = new HttpClient(handler);
PerformRequest(client);
I've seen a dozen SO posts related to this, but none have resolved my issue.
Things to note:
HttpClient
is not sending one.handler.SslProtocols
to 1.0, 1.1, 1.2 -- none workX509Certificate2
returned from GetClientCertificate()
has a private keyI have been troubleshooting this for 3 days now and even read through the reference source trying to figure out why my cert isn't included, but I can't find a reason.
I suspect somewhere deep inside the HttpClient
implementation, my cert is getting rejected because it is self-signed.
How can I force it to send my cert? (the server maintains a whitelist, so I don't care if it thinks my cert is invalid). Or, at bare minimum, how can I get any sort of debugging information out of this? Is there any way to get a reason why a cert is rejected?
Update:
After enabling tracing, I have found that .NET is correctly selecting my certificate:
System.Net Information: 0 : [23960] SecureChannel#64538993 - Selected certificate: <omitted>
System.Net Information: 0 : [23960] SecureChannel#64538993 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [23960] SecureChannel#64538993 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [23960] SecureChannel#64538993 - Locating the private key for the certificate: <omitted>
System.Net Information: 0 : [23960] SecureChannel#64538993 - Certificate is of type X509Certificate2 and contains the private key.
System.Net Information: 0 : [23960] SecureChannel#64538993::.AcquireClientCredentials, new SecureCredential() (flags=(ValidateManual, NoDefaultCred, SendAuxRecord), m_ProtocolFlags=(Tls12Client), m_EncryptionPolicy=RequireEncryption)
System.Net Information: 0 : [23960] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent = Outbound, scc = System.Net.SecureCredential)
System.Net Information: 0 : [23960] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 21c5cd98:21cd2108, targetName = <omitted>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [23960] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=100, returned code=ContinueNeeded).
... etc
However, it is still not being sent. Any more ideas on where to look would be greatly appreciated.
This answer led me to the solution.
X509Certificate2.PrivateKey
was throwing a NotSupportedException
because I was using ECD as the signature algorithm. I switched to RSA and now it properly sends the certificate.
What's strange is that tracing showed no issue with the ECD cert, and it was able to successfully identify the private key. I have no idea why this is the case -- it sounds like a bug to me. Nevertheless, RSA will work for my use case and I am tired of debugging this.