Search code examples
c#.netubuntuiispfx

HttpClient using PFX transport certificate to stablish connection to third part application


I have an Asp.Net core app which has a lot of PFX certificates, those certificates are added as resource to the application, each certificate has a specific client. This Asp.net app is responsible to validate the user through username/password, retrieve the stored PFX certificate and submit a POST request to another application, which I don`t have access to.

Everything works fine when running on localhost, however, when I deploy the solution(I`m deploying it in Ubuntu and IIS) I receive SSL exception which states the connection was not possible to be stablish.

I have performed a few tests and came to the conclusion that it is not the third part server which is refusing connection, but the deployment server. Could someone provide me some help on how to solve this?

this is my C# code:

X509Certificate2 certificate;
string certificatePassword = "myPassword";

var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var resourceName = "MyApplication.identification_certificate.pfx";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
  byte[] bytes = new byte[stream.Length];
  await stream.ReadAsync(bytes, 0, bytes.Length);
  certificate = new X509Certificate2(bytes, certificatePassword);
}


HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(certificate);
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;

using (HttpClient client = new HttpClient(handler))
{
 // Set the content type
 client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

 // Make the request
 string url = _configuration["Company"] + "/GetServicedUserInfo/";
 HttpResponseMessage rsp = await client.PostAsync(url, new StringContent(xmlRequest, Encoding.UTF8, "application/xml"));  //<< Exception happens here
 string responseContent = await rsp.Content.ReadAsStringAsync();
}
response.Structure = JsonConvert.SerializeObject(clientResponse);

Ubuntu exception message:

InnerException = {System.Net.Sockets.SocketException (104): Connection reset by peer at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.CreateException(SocketError error, Boolean forAsyncThrow) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.SendAsy...

If someone could provide me an insight, I would really appreciate it! thank you in advance..

Manually install the certificate, add the certificate into trusted authority, change the call from RestSharp to System.Net.Http, Use one of the PFX as DNS SSL server


Solution

  • As commented by @Charlieface, have a look here.

    You see the exception in PostAsync because .NET initiates and negotiates SSL protocol with the server under the hood. To peek at that, you'll need to expand ServerCertificateCustomValidationCallback, for example:

            httpClientHandler.ServerCertificateCustomValidationCallback += (_, cert, chain, policyErrors) =>
            {
                if (policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable))
                {
                    // certificate not provided for validation; cannot continue
                    return false;
                }
    
                if (policyErrors is SslPolicyErrors.None)
                {
                    // certificate accepted
                    return true;
                }
    
                if (policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
                {
                    // certificate failed CN name check
                }
    
                var hasChainErrors = policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors);
                if (hasChainErrors)
                {
                    // certificate generated policy error(s)
                    // log using policyErrors.ToString()
    
                    // certificate rejected (HTTPS reject)
                    return false;
                }
    
                // certificate accepted (TLS check passed)
                return true;
            };