0

I have two different projects, one written in .Net 4.8 and another in .Net6.

The code works fine in .net 4.8, but fails in .net6. The exceptions I get, and it's inner exceptions, is

The SSL connection could not be established, see inner exception

Authentication failed, see inner exception

The token supplied to the function is invalid

The code is written in the same way, no difference at all. First the part for the System.Net.Http.HttpClient _httpRequestClient:

        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
        string baseAddress = host;

        string credentials = Convert.ToBase64String(new ASCIIEncoding().GetBytes(string.Format("{0}:{1}", connection.Username, connection.Password)));


        _httpRequestClient = new HttpClient();
        _httpRequestClient.BaseAddress = new Uri(baseAddress);
        _httpRequestClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("basic",
                 credentials);

        _httpRequestClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

And when making a call to the server from an async Task it throws the exeption:

using (var response = await _httpRequestClient.SendAsync(
                    new HttpRequestMessage(HttpMethod.Get, command), HttpCompletionOption.ResponseHeadersRead)
                    )

While debugging, the _httpRequestClient.DefaultRequestHeaders.Authorization is exactly the same in both projects (as well as most of the values) except that in .net6 the _httpRequestClient also has DefaultRequestVersion and DefaultVersionPolicy.

For now at least, I really cannot figure out what's wrong. Please help :)

XenoPsy
  • 93
  • 1
  • 1
  • 6
  • 3
    Seeing `ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };` is scary. It basically ignores any SSL certificates. – ProgrammingLlama Aug 26 '22 at 10:16
  • 1
    [Relevant](https://stackoverflow.com/questions/40939031/tls-1-2-the-token-supplied-to-the-function-is-invalid)? – canton7 Aug 26 '22 at 10:23
  • 1
    Also `ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;` why? That is very possibly your issue. And `new ASCIIEncoding().GetBytes(string.Format("{0}:{1}", connection.Username, connection.Password))` you don't need to new up the encoding you can do `Encoding.ASCII`, and depending on your server you probably actually want `Encoding.UTF8.GetBytes($"{connection.Username}:{connection.Password}")` – Charlieface Aug 26 '22 at 10:47
  • 1
    @DiplomacyNotWar, this is only for now. More appropriate control are added later. Thanks anyway :) – XenoPsy Aug 26 '22 at 11:05
  • @Charlieface, Thanks for your input. However, if I remove ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;I get a TLS error. Or if I change it as well. – XenoPsy Aug 26 '22 at 13:17
  • 1
    Does this answer your question? [TLS 1.2 - The token supplied to the function is invalid](https://stackoverflow.com/questions/40939031/tls-1-2-the-token-supplied-to-the-function-is-invalid) – Charlieface Aug 28 '22 at 01:05

1 Answers1

1

First of, thanks for pointing me to the right direction.

However, .Net framework and .Net standard has some differences.

In .net standard, the following code doesn't seem to do anything in my usage:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;

Instead, I had to create an instance of HttpClientHandler and pass it to the HttpClient constructor. See below:

        _httpClientHandler = new HttpClientHandler();
        _httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
        _httpClientHandler.ServerCertificateCustomValidationCallback =
            (httpRequestMessage, cert, cetChain, policyErrors) =>
            {
                //TODO Add custom validation
                return true;
            };


        string credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{connection.Username}:{connection.Password}"));

        //Added _httpClientHandler to constructor
        _httpRequestClient = new HttpClient(_httpClientHandler);
        _httpRequestClient.BaseAddress = new Uri(baseAddress);
        _httpRequestClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("basic",
                 credentials);

        _httpRequestClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

The final, and main hint to what was wrong, was found here: https://stackoverflow.com/a/46626858/2488982

Again, thank everyone :)

Edit: Certificate validation should not be tampered with. If self signed certificates are to be used, add them to trusted CA for the clients.

This means my code has been reduced to this:

string credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{connection.Username}:{connection.Password}"));


        _httpRequestClient = new HttpClient();
        _httpRequestClient.BaseAddress = new Uri(baseAddress);
        _httpRequestClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("basic",
                 credentials);

        _httpRequestClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        _httpRequestClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
        _httpRequestClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/vnd.assaabloy.arx.integrated-alarm-1.0+json"));

No custom validation is done anymore.

Thanks Panagiotis Kanavos for your input.

XenoPsy
  • 93
  • 1
  • 1
  • 6
  • 1
    This isn't a solution this covers up the real problem - the server uses an obsolete SSL version. The *real* solution is to fix the server so it uses TLS1.2. *And* use a valid certificate instead of disabling validation. All major cloud services like Google, AWS, Azure, disabled anything less than TLS1.2 5 years ago – Panagiotis Kanavos Aug 29 '22 at 08:20
  • 1
    Disabling certificate validation essentially negates HTTPS. HTTPS isn't meant to encrypt connections, it's meant to verify that you're connecting to the correct server instead of a hacker's HTTPS proxy using a self-signed certificate – Panagiotis Kanavos Aug 29 '22 at 08:21
  • @PanagiotisKanavos, I agree. Sadly, my access to this server is limited. This is only run on a local network (with limited access). However, I managed to make them upgrade to Tls1.3, but still a self signed cert. For now we validate the thumbprint and the issuer until they create a valid cert. – XenoPsy Aug 29 '22 at 09:07
  • 1
    `A local network` that's how all public breaches start - a hacker gains enough access to install a fake proxy and start intercepting calls. You can use a self-signed certificate securely though. In an Active Directory domain, the certificates generated by AD Certificate Services are trusted *inside the domain* because the issuing authority is the domain itself. Even if you create certificates from the command line, they could be trusted if the root certificate is properly protected and added to the list of trusted CAs used by all machines or at least the clients – Panagiotis Kanavos Aug 29 '22 at 09:13
  • @PanagiotisKanavos. It's seems then that I approached this issue from the wrong angle to begin with then. I added the certificate to trusted CAs and now I don't have to fiddle with HttpClientHandler nor ValidationCallback. I will correct my anwser above. Thank you :) – XenoPsy Aug 29 '22 at 09:58