2

Adding client certificates to a typed HttpClient in .net 3.1 linux docker container works, but fails on upgrading to .net 5. I see that there is a breaking change in .net 5 to use TLS 1.3 and restricted Ciphersuites, how can I override that in .net 5 with a typed HttpClient ?
How can I use the SocketsHttpHandler.SslOptions with a typed HttpClient to specify a wider set of Ciphersuites. The site am try to send a payment request supports only tls 1.2 and its ssllabs report is this https://www.ssllabs.com/ssltest/analyze.html?d=mss.cpc.getswish.net

This is a sample of my code

 services.AddHttpClient<ISwishpayService, SwishpayService>()
 .ConfigurePrimaryHttpMessageHandler<SwishpayHandler>();
public class SwishpayHandler: HttpClientHandler
    {
        private readonly IConfiguration _config;
        private readonly ILogger<SwishpayHandler> _logger;
        public SwishpayHandler(IConfiguration config, ILogger<SwishpayHandler> logger)
        {
            _config = config;
            _logger = logger;
              SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13;
            ClientCertificateOptions = ClientCertificateOption.Manual;
        }
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {            
            
            if (ClientCertificates == null || ClientCertificates.Count == 0)
            {
               _logger.LogInformation("Invoked SwishpayHandler");
                using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser, OpenFlags.ReadWrite))
                {                  
                    var certs = new X509Certificate2Collection();
                    certs.Import(Path.Combine("Certificates", _config.GetValue<string>("SwishApi:key:certificatefile")), GetCertificatePassword(), X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
                    
                    foreach (X509Certificate2 cert in certs)
                    {
                        if (cert.HasPrivateKey)
                        {
                            ClientCertificates.Add(cert);
                        }
                        else
                        {
                            store.Add(cert);
                        }
                    }
                    store.Close();
                }
            }

            return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
        }

        private string GetCertificatePassword()
        {          
            var cert_password = File.ReadAllText(_config.GetValue<string>("SWISHPAY_CERT_PWD").Trim()).Replace(Environment.NewLine, "");

            return cert_password;
        }
    }

UPDATE

Exception in .net 5

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
 ---> Interop+Crypto+OpenSslCryptographicException: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
  • The SslLabs report for the supported ciphersuites of that endpoint is the same list as the default ciphersuites for .NET 5 on Linux, so there should be a match. Something else must be going on, you'll probably have to get a network trace to see where the failure happened. – bartonjs Feb 25 '21 at 16:57
  • @bartonjs analyzing further noticed that the SSL handshake works on application startup but if left inactive for 2 minutes when a new instance of HttpClientHandler is created by the Factory method the SSL handshake fails. Is it possible that the certificates dont get added on the instances created by the HttpClientFactory? This behavior does not occur in 3.1 – Kiruthiga Muthuswamy Feb 25 '21 at 19:40
  • Hi @KiruthigaMuthuswamy, Did you ever resolve this issue? I'm experiencing the exact same problem... – grimdog_john Aug 10 '21 at 15:24

2 Answers2

1

There are several issues with your code which isn't related to .NET 5.

Issue #1:

X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser, OpenFlags.ReadWrite)

why you search for client certificates in StoreName.CertificateAuthority? It is incorrect, store name MUST be StoreName.My.

Issue #2:

certs.Import(Path.Combine("Certificates", _config.GetValue<string>("SwishApi:key:certificatefile")), GetCertificatePassword(), X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

Sotre is opened in CurrentUser context, but you specify X509KeyStorageFlags.MachineKeySet for some unknown reason. Why? It MUST be X509KeyStorageFlags.UserKeySet.

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • The issue occurs even if I don't use X509Store for getting the entire chain , CertificateAuthority was to create a temporary cache for the entire chain. I tried with StoreName.My that also does not work. Using X509KeyStorageFlags.DefaultKeySet did not help get , so tried using the MachineKeySet OR PersistKeySet. The X509 is based on another post https://stackoverflow.com/questions/61677247/can-a-p12-file-with-ca-certificates-be-used-in-c-sharp-without-importing-them-t/66346983#66346983 – Kiruthiga Muthuswamy Feb 25 '21 at 07:51
  • Tested with StoreName.My and X509KeyStorageFlags.UserKeySet, but getting the same issue – Kiruthiga Muthuswamy Feb 25 '21 at 08:25
  • Once you fix these issues in your code, you can debug it further. You didn't tell what exactly fails, no exception/stacktrace details. – Crypt32 Feb 25 '21 at 08:36
  • Updated my original post with the exception that occurs in .net 5 – Kiruthiga Muthuswamy Feb 25 '21 at 09:52
0

Try the following,

 using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser, OpenFlags.ReadWrite))
        {
            var certs = new X509Certificate2Collection();
            certs.Import(settings.Value.ClientCertPath, settings.Value.ClientCertSecret, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

            foreach (X509Certificate2 cert in certs)
            {
                if (cert.HasPrivateKey)
                {
                    ClientCertificates.Add(cert);
                    logger.LogInformation("CertTest: " + cert.Thumbprint);
                }
                else
                {
                    store.Add(cert);
                    logger.LogInformation("CertTest store: " + cert.Thumbprint);
                }
            }
            store.Close();
        }
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 05 '22 at 08:57